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: IpatchSF2
22  * @short_description: SoundFont instrument file object
23  * @see_also: #IpatchSF2Preset, #IpatchSF2Inst, #IpatchSF2Sample
24  * @stability: Stable
25  *
26  * SoundFont version 2 instrument file object.  Parent to #IpatchSF2Preset,
27  * #IpatchSF2Inst and #IpatchSF2Sample objects.
28  */
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <glib.h>
33 #include <glib-object.h>
34 #include "IpatchParamProp.h"
35 #include "IpatchSF2.h"
36 #include "IpatchSF2File.h"
37 #include "IpatchSF2Preset.h"
38 #include "IpatchSF2Zone.h"
39 #include "IpatchTypeProp.h"
40 #include "IpatchVirtualContainer_types.h"
41 #include "ipatch_priv.h"
42 #include "version.h"
43 
44 /* the Info properties below shouldn't conflict, since they are composed of
45    their 4 byte RIFF chunk ID */
46 enum
47 {
48     PROP_0,
49     PROP_SAMPLES_24BIT	/* indicates that samples should be saved in 24 bit */
50 };
51 
52 /* !! Keep in order with IpatchSF2InfoType in IpatchSF2.h */
53 static guint info_ids[] =
54 {
55     IPATCH_SF2_VERSION, IPATCH_SF2_ENGINE, IPATCH_SF2_NAME,
56     IPATCH_SF2_ROM_NAME, IPATCH_SF2_ROM_VERSION, IPATCH_SF2_DATE,
57     IPATCH_SF2_AUTHOR, IPATCH_SF2_PRODUCT, IPATCH_SF2_COPYRIGHT,
58     IPATCH_SF2_COMMENT, IPATCH_SF2_SOFTWARE
59 };
60 
61 /* parameter specs for each info property */
62 static GParamSpec *info_prop_pspecs[IPATCH_SF2_INFO_COUNT];
63 
64 
65 #define ERR_MSG_INVALID_INFO_ID_1 "Invalid SoundFont info ID (%d)"
66 
67 static void ipatch_sf2_class_init(IpatchSF2Class *klass);
68 static void ipatch_sf2_init(IpatchSF2 *sfont);
69 static void ipatch_sf2_finalize(GObject *gobject);
70 static void ipatch_sf2_set_property(GObject *object, guint property_id,
71                                     const GValue *value, GParamSpec *pspec);
72 static void ipatch_sf2_get_property(GObject *object, guint property_id,
73                                     GValue *value, GParamSpec *pspec);
74 static void ipatch_sf2_item_copy(IpatchItem *dest, IpatchItem *src,
75                                  IpatchItemCopyLinkFunc link_func,
76                                  gpointer user_data);
77 static void ipatch_sf2_info_hash_foreach(gpointer key, gpointer value,
78         gpointer user_data);
79 
80 static const GType *ipatch_sf2_container_child_types(void);
81 static const GType *ipatch_sf2_container_virtual_types(void);
82 static gboolean ipatch_sf2_container_init_iter(IpatchContainer *container,
83         IpatchIter *iter, GType type);
84 static void ipatch_sf2_container_make_unique(IpatchContainer *container,
85         IpatchItem *item);
86 static void ipatch_sf2_base_find_unused_locale(IpatchBase *base, int *bank,
87         int *program,
88         const IpatchItem *exclude,
89         gboolean percussion);
90 static int locale_gcompare_func(gconstpointer a, gconstpointer b);
91 static IpatchItem *
92 ipatch_sf2_base_find_item_by_locale(IpatchBase *base, int bank, int program);
93 static void ipatch_sf2_real_set_info(IpatchSF2 *sf, IpatchSF2InfoType id,
94                                      const char *val);
95 static void ipatch_sf2_foreach_info_GHFunc(gpointer key, gpointer value,
96         gpointer data);
97 static int ipatch_sf2_info_array_qsort(const void *a, const void *b);
98 
99 static gpointer parent_class = NULL;
100 
101 static GType sf2_child_types[4] = { 0 };
102 static GType sf2_virt_types[6] = { 0 };
103 
104 
105 /* SoundFont item type creation function */
106 GType
ipatch_sf2_get_type(void)107 ipatch_sf2_get_type(void)
108 {
109     static GType item_type = 0;
110 
111     if(!item_type)
112     {
113         static const GTypeInfo item_info =
114         {
115             sizeof(IpatchSF2Class), NULL, NULL,
116             (GClassInitFunc)ipatch_sf2_class_init, NULL, NULL,
117             sizeof(IpatchSF2), 0,
118             (GInstanceInitFunc)ipatch_sf2_init,
119         };
120 
121         item_type = g_type_register_static(IPATCH_TYPE_BASE, "IpatchSF2",
122                                            &item_info, 0);
123     }
124 
125     return (item_type);
126 }
127 
128 static void
ipatch_sf2_class_init(IpatchSF2Class * klass)129 ipatch_sf2_class_init(IpatchSF2Class *klass)
130 {
131     GObjectClass *obj_class = G_OBJECT_CLASS(klass);
132     IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
133     IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass);
134     IpatchBaseClass *base_class = IPATCH_BASE_CLASS(klass);
135     GParamSpec **sp = &info_prop_pspecs[0];
136 
137     parent_class = g_type_class_peek_parent(klass);
138 
139     obj_class->finalize = ipatch_sf2_finalize;
140     obj_class->get_property = ipatch_sf2_get_property;
141 
142     /* we use the IpatchItem item_set_property method */
143     item_class->item_set_property = ipatch_sf2_set_property;
144     item_class->copy = ipatch_sf2_item_copy;
145 
146     container_class->child_types = ipatch_sf2_container_child_types;
147     container_class->virtual_types = ipatch_sf2_container_virtual_types;
148     container_class->init_iter = ipatch_sf2_container_init_iter;
149     container_class->make_unique = ipatch_sf2_container_make_unique;
150 
151     base_class->find_unused_locale = ipatch_sf2_base_find_unused_locale;
152     base_class->find_item_by_locale = ipatch_sf2_base_find_item_by_locale;
153 
154     g_object_class_install_property(obj_class, PROP_SAMPLES_24BIT,
155                                     g_param_spec_boolean("samples-24bit", _("Samples 24bit"),
156                                             _("Enable 24 bit samples"), FALSE, G_PARAM_READWRITE));
157 
158     g_object_class_override_property(obj_class, IPATCH_SF2_NAME, "title");
159 
160     *sp = g_param_spec_string("version", _("Version"),
161                               _("SoundFont version (\"major.minor\")"), "2.01", G_PARAM_READWRITE);
162     g_object_class_install_property(obj_class, IPATCH_SF2_VERSION, *sp++);
163 
164     *sp = ipatch_param_set(g_param_spec_string("engine", _("Engine"),
165                            _("Sound synthesis engine identifier"), "EMU8000", G_PARAM_READWRITE),
166                            "string-max-length", 255, NULL);
167     g_object_class_install_property(obj_class, IPATCH_SF2_ENGINE, *sp++);
168 
169     *sp = ipatch_param_set(g_param_spec_string("name", _("Name"),
170                            _("SoundFont name"), NULL, G_PARAM_READWRITE),
171                            "string-max-length", 255, NULL);
172     g_object_class_install_property(obj_class, IPATCH_SF2_NAME, *sp++);
173 
174     *sp = ipatch_param_set(g_param_spec_string("rom-name", _("ROM name"),
175                            _("ROM name identifier"), NULL, G_PARAM_READWRITE),
176                            "string-max-length", 255, NULL);
177     g_object_class_install_property(obj_class, IPATCH_SF2_ROM_NAME, *sp++);
178 
179     *sp = ipatch_param_set(g_param_spec_string("rom-version", _("ROM version"),
180                            _("ROM version \"major.minor\""), NULL, G_PARAM_READWRITE),
181                            "string-max-length", 255, NULL);
182     g_object_class_install_property(obj_class, IPATCH_SF2_ROM_VERSION, *sp++);
183 
184     *sp = ipatch_param_set(g_param_spec_string("date", _("Date"),
185                            _("Creation date"), NULL, G_PARAM_READWRITE),
186                            "string-max-length", 255, NULL);
187     g_object_class_install_property(obj_class, IPATCH_SF2_DATE, *sp++);
188 
189     *sp = ipatch_param_set(g_param_spec_string("author", _("Author"),
190                            _("Author of SoundFont"), NULL, G_PARAM_READWRITE),
191                            "string-max-length", 255, NULL);
192     g_object_class_install_property(obj_class, IPATCH_SF2_AUTHOR, *sp++);
193 
194     *sp = ipatch_param_set(g_param_spec_string("product", _("Product"),
195                            _("Product SoundFont is intended for"), NULL, G_PARAM_READWRITE),
196                            "string-max-length", 255, NULL);
197     g_object_class_install_property(obj_class, IPATCH_SF2_PRODUCT, *sp++);
198 
199     *sp = ipatch_param_set(g_param_spec_string("copyright", _("Copyright"),
200                            _("Copyright"), NULL, G_PARAM_READWRITE),
201                            "string-max-length", 255, NULL);
202     g_object_class_install_property(obj_class, IPATCH_SF2_COPYRIGHT, *sp++);
203 
204     *sp = ipatch_param_set(g_param_spec_string("comment", _("Comments"),
205                            _("Comments"), NULL, G_PARAM_READWRITE),
206                            "string-max-length", 65535, NULL);
207     g_object_class_install_property(obj_class, IPATCH_SF2_COMMENT, *sp++);
208 
209     *sp = ipatch_param_set(g_param_spec_string("software", _("Software"),
210                            _("Software 'created by:modified by'"), NULL, G_PARAM_READWRITE),
211                            "string-max-length", 255, NULL);
212     g_object_class_install_property(obj_class, IPATCH_SF2_SOFTWARE, *sp);
213 
214     sf2_child_types[0] = IPATCH_TYPE_SF2_PRESET;
215     sf2_child_types[1] = IPATCH_TYPE_SF2_INST;
216     sf2_child_types[2] = IPATCH_TYPE_SF2_SAMPLE;
217 
218     sf2_virt_types[0] = IPATCH_TYPE_VIRTUAL_SF2_MELODIC;
219     sf2_virt_types[1] = IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION;
220     sf2_virt_types[2] = IPATCH_TYPE_VIRTUAL_SF2_INST;
221     sf2_virt_types[3] = IPATCH_TYPE_VIRTUAL_SF2_SAMPLES;
222     sf2_virt_types[4] = IPATCH_TYPE_VIRTUAL_SF2_ROM;
223 }
224 
225 static void
ipatch_sf2_init(IpatchSF2 * sfont)226 ipatch_sf2_init(IpatchSF2 *sfont)
227 {
228     sfont->ver_major = 2;
229     sfont->ver_minor = 1;
230 
231     /* we convert 4 char info IDs to INTs, also set up hash table to free
232        allocated string values */
233     sfont->info = g_hash_table_new_full(NULL, NULL, NULL,
234                                         (GDestroyNotify)g_free);
235 
236     /* set required SoundFont info to default values */
237     ipatch_sf2_set_info(sfont, IPATCH_SF2_NAME, _(IPATCH_BASE_DEFAULT_NAME));
238     ipatch_sf2_set_info(sfont, IPATCH_SF2_ENGINE, IPATCH_SF2_DEFAULT_ENGINE);
239     ipatch_sf2_set_info(sfont, IPATCH_SF2_SOFTWARE,
240                         "libInstPatch v" IPATCH_VERSION ":");
241 
242     ipatch_item_clear_flags(IPATCH_ITEM(sfont), IPATCH_BASE_CHANGED);
243 }
244 
245 /* function called when SoundFont is being destroyed */
246 static void
ipatch_sf2_finalize(GObject * gobject)247 ipatch_sf2_finalize(GObject *gobject)
248 {
249     IpatchSF2 *sf = IPATCH_SF2(gobject);
250 
251     IPATCH_ITEM_WLOCK(sf);
252 
253     g_hash_table_destroy(sf->info);	/* destroy SoundFont info */
254     sf->info = NULL;
255 
256     IPATCH_ITEM_WUNLOCK(sf);
257 
258     if(G_OBJECT_CLASS(parent_class)->finalize)
259     {
260         G_OBJECT_CLASS(parent_class)->finalize(gobject);
261     }
262 }
263 
264 static void
ipatch_sf2_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)265 ipatch_sf2_set_property(GObject *object, guint property_id,
266                         const GValue *value, GParamSpec *pspec)
267 {
268     IpatchSF2 *sf;
269     gboolean b;
270 
271     g_return_if_fail(IPATCH_IS_SF2(object));
272     sf = IPATCH_SF2(object);
273 
274     if(property_id == PROP_SAMPLES_24BIT)
275     {
276         b = g_value_get_boolean(value);
277 
278         if(b)
279         {
280             ipatch_item_set_flags(IPATCH_ITEM(sf), IPATCH_SF2_SAMPLES_24BIT);
281         }
282         else
283         {
284             ipatch_item_clear_flags(IPATCH_ITEM(sf), IPATCH_SF2_SAMPLES_24BIT);
285         }
286     }
287     else if(property_id == IPATCH_SF2_VERSION
288             || property_id == IPATCH_SF2_ROM_VERSION)
289     {
290         guint major, minor;
291 
292         if(sscanf(g_value_get_string(value), "%u.%u",
293                   &major, &minor) != 2)
294         {
295             g_critical("SoundFont version property parse error");
296             return;
297         }
298 
299         if(property_id == IPATCH_SF2_VERSION)
300         {
301             IPATCH_ITEM_WLOCK(sf);
302             sf->ver_major = major;
303             sf->ver_minor = minor;
304             IPATCH_ITEM_WUNLOCK(sf);
305         }
306         else
307         {
308             IPATCH_ITEM_WLOCK(sf);
309             sf->romver_major = major;
310             sf->romver_minor = minor;
311             IPATCH_ITEM_WUNLOCK(sf);
312         }
313     }
314     else if(ipatch_sf2_info_id_is_valid(property_id))
315     {
316         ipatch_sf2_real_set_info(sf, property_id, g_value_get_string(value));
317 
318         /* need to do a title property notify? */
319         if(property_id == IPATCH_SF2_NAME)
320             ipatch_item_prop_notify((IpatchItem *)sf, ipatch_item_pspec_title,
321                                     value, NULL);
322     }
323     else
324     {
325         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
326     }
327 }
328 
329 static void
ipatch_sf2_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)330 ipatch_sf2_get_property(GObject *object, guint property_id,
331                         GValue *value, GParamSpec *pspec)
332 {
333     IpatchSF2 *sf;
334     char *s;
335 
336     g_return_if_fail(IPATCH_IS_SF2(object));
337     sf = IPATCH_SF2(object);
338 
339     if(property_id == PROP_SAMPLES_24BIT)
340     {
341         g_value_set_boolean(value, (ipatch_item_get_flags(IPATCH_ITEM(sf))
342                                     & IPATCH_SF2_SAMPLES_24BIT) != 0);
343     }
344     else if(property_id == IPATCH_SF2_VERSION
345             || property_id == IPATCH_SF2_ROM_VERSION)
346     {
347         int major, minor;
348 
349         IPATCH_ITEM_RLOCK(sf);
350 
351         if(property_id == IPATCH_SF2_VERSION)
352         {
353             major = sf->ver_major;
354             minor = sf->ver_minor;
355         }
356         else
357         {
358             major = sf->romver_major;
359             minor = sf->romver_minor;
360         }
361 
362         IPATCH_ITEM_RUNLOCK(sf);
363 
364         s = g_strdup_printf("%d.%d", major, minor);
365         g_value_take_string(value, s);
366     }
367     else if(ipatch_sf2_info_id_is_valid(property_id))
368     {
369         g_value_take_string(value, ipatch_sf2_get_info(sf, property_id));
370     }
371     else
372     {
373         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
374     }
375 }
376 
377 /* item copy function, note that this is an #IpatchBase derived object, so
378    link_func is not used */
379 static void
ipatch_sf2_item_copy(IpatchItem * dest,IpatchItem * src,IpatchItemCopyLinkFunc link_func,gpointer user_data)380 ipatch_sf2_item_copy(IpatchItem *dest, IpatchItem *src,
381                      IpatchItemCopyLinkFunc link_func,
382                      gpointer user_data)
383 {
384     IpatchSF2 *src_sf, *dest_sf;
385     IpatchItem *newitem;
386     GHashTable *repl_samples, *repl_insts;
387     gboolean has_linked = FALSE;
388     GSList *p;
389 
390     src_sf = IPATCH_SF2(src);
391     dest_sf = IPATCH_SF2(dest);
392 
393     /* create item replacement hash */
394     repl_samples = g_hash_table_new(NULL, NULL);
395     repl_insts = g_hash_table_new(NULL, NULL);
396 
397     IPATCH_ITEM_RLOCK(src_sf);
398 
399     if(ipatch_item_get_flags(src) & IPATCH_SF2_SAMPLES_24BIT)
400     {
401         ipatch_item_set_flags(dest, IPATCH_SF2_SAMPLES_24BIT);
402     }
403     else
404     {
405         ipatch_item_clear_flags(dest, IPATCH_SF2_SAMPLES_24BIT);
406     }
407 
408     dest_sf->ver_major = src_sf->ver_major;
409     dest_sf->ver_minor = src_sf->ver_minor;
410     dest_sf->romver_major = src_sf->romver_major;
411     dest_sf->romver_minor = src_sf->romver_minor;
412 
413     if(IPATCH_BASE(src_sf)->file)
414     {
415         ipatch_base_set_file(IPATCH_BASE(dest_sf), IPATCH_BASE(src_sf)->file);
416     }
417 
418     /* duplicate the info variables */
419     g_hash_table_foreach(src_sf->info, ipatch_sf2_info_hash_foreach, dest_sf);
420 
421     p = src_sf->samples;
422 
423     while(p)			/* duplicate samples */
424     {
425         /* ++ ref new duplicate sample, !! sample list takes it over */
426         newitem = ipatch_item_duplicate((IpatchItem *)(p->data));
427         dest_sf->samples = g_slist_prepend(dest_sf->samples, newitem);
428         ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sf));
429 
430         /* add to sample pointer replacement hash */
431         g_hash_table_insert(repl_samples, p->data, newitem);
432 
433         /* check if sample is stereo linked */
434         if(ipatch_sf2_sample_peek_linked((IpatchSF2Sample *)newitem))
435         {
436             has_linked = TRUE;
437         }
438 
439         p = g_slist_next(p);
440     }
441 
442     /* if any linked samples exist, we must replace old linked pointers,
443        duplicate "replace" hash wont work since samples reference items in the
444        same list */
445     if(has_linked)
446     {
447         IpatchSF2Sample *linked;
448 
449         p = dest_sf->samples;
450 
451         while(p)
452         {
453             IpatchSF2Sample *sample = (IpatchSF2Sample *)(p->data);
454 
455             linked = ipatch_sf2_sample_peek_linked(sample);
456 
457             if(linked)
458             {
459                 linked = g_hash_table_lookup(repl_samples, linked);
460                 ipatch_sf2_sample_set_linked(sample, linked);
461             }
462 
463             p = g_slist_next(p);
464         }
465     }
466 
467     p = src_sf->insts;
468 
469     while(p)			/* duplicate instruments */
470     {
471         /* ++ ref new duplicate instrument, !! instrument list takes it over
472          * duplicate instrument and replace referenced sample pointers. */
473         newitem = ipatch_item_duplicate_replace((IpatchItem *)(p->data),
474                                                 repl_samples);
475         dest_sf->insts = g_slist_prepend(dest_sf->insts, newitem);
476         ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sf));
477 
478         /* add to instrument pointer replacement hash */
479         g_hash_table_insert(repl_insts, p->data, newitem);
480 
481         p = g_slist_next(p);
482     }
483 
484     p = src_sf->presets;
485 
486     while(p)			/* duplicate presets */
487     {
488         /* ++ ref new duplicate preset, !! preset list takes it over
489          * duplicate preset and replace referenced instrument pointers. */
490         newitem = ipatch_item_duplicate_replace((IpatchItem *)(p->data),
491                                                 repl_insts);
492         dest_sf->presets = g_slist_prepend(dest_sf->presets, newitem);
493         ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sf));
494 
495         p = g_slist_next(p);
496     }
497 
498     IPATCH_ITEM_RUNLOCK(src_sf);
499 
500     dest_sf->presets = g_slist_reverse(dest_sf->presets);
501     dest_sf->insts = g_slist_reverse(dest_sf->insts);
502     dest_sf->samples = g_slist_reverse(dest_sf->samples);
503 
504     g_hash_table_destroy(repl_samples);
505     g_hash_table_destroy(repl_insts);
506 }
507 
508 static void
ipatch_sf2_info_hash_foreach(gpointer key,gpointer value,gpointer user_data)509 ipatch_sf2_info_hash_foreach(gpointer key, gpointer value,
510                              gpointer user_data)
511 {
512     char *val = (char *)value;
513     IpatchSF2 *dup = (IpatchSF2 *)user_data;
514 
515     ipatch_sf2_set_info(dup, GPOINTER_TO_UINT(key), val);
516 }
517 
518 static const GType *
ipatch_sf2_container_child_types(void)519 ipatch_sf2_container_child_types(void)
520 {
521     return (sf2_child_types);
522 }
523 
524 static const GType *
ipatch_sf2_container_virtual_types(void)525 ipatch_sf2_container_virtual_types(void)
526 {
527     return (sf2_virt_types);
528 }
529 
530 /* container is locked by caller */
531 static gboolean
ipatch_sf2_container_init_iter(IpatchContainer * container,IpatchIter * iter,GType type)532 ipatch_sf2_container_init_iter(IpatchContainer *container,
533                                IpatchIter *iter, GType type)
534 {
535     IpatchSF2 *sfont = IPATCH_SF2(container);
536 
537     if(g_type_is_a(type, IPATCH_TYPE_SF2_PRESET))
538     {
539         ipatch_iter_GSList_init(iter, &sfont->presets);
540     }
541     else if(g_type_is_a(type, IPATCH_TYPE_SF2_INST))
542     {
543         ipatch_iter_GSList_init(iter, &sfont->insts);
544     }
545     else if(g_type_is_a(type, IPATCH_TYPE_SF2_SAMPLE))
546     {
547         ipatch_iter_GSList_init(iter, &sfont->samples);
548     }
549     else
550     {
551         g_critical("Invalid child type '%s' for parent of type '%s'",
552                    g_type_name(type), g_type_name(G_OBJECT_TYPE(container)));
553         return (FALSE);
554     }
555 
556     return (TRUE);
557 }
558 
559 static void
ipatch_sf2_container_make_unique(IpatchContainer * container,IpatchItem * item)560 ipatch_sf2_container_make_unique(IpatchContainer *container, IpatchItem *item)
561 {
562     IpatchSF2 *sfont = IPATCH_SF2(container);
563     char *name, *newname;
564 
565     IPATCH_ITEM_WLOCK(sfont);
566 
567     if(IPATCH_IS_SF2_PRESET(item))
568     {
569         int bank, newbank, program, newprogram;
570         ipatch_sf2_preset_get_midi_locale(IPATCH_SF2_PRESET(item),
571                                           &bank, &program);
572         newbank = bank;
573         newprogram = program;
574 
575         ipatch_base_find_unused_midi_locale(IPATCH_BASE(sfont),
576                                             &newbank, &newprogram, item,
577                                             newbank == 128);
578 
579         if(bank != newbank || program != newprogram)
580             ipatch_sf2_preset_set_midi_locale(IPATCH_SF2_PRESET(item),
581                                               newbank, newprogram);
582     }
583     else if(!IPATCH_IS_SF2_INST(item) && !IPATCH_IS_SF2_SAMPLE(item))
584     {
585         g_critical("Invalid child type '%s' for IpatchSF2 object",
586                    g_type_name(G_TYPE_FROM_INSTANCE(item)));
587         return;
588     }
589 
590     g_object_get(item, "name", &name, NULL);
591     newname = ipatch_sf2_make_unique_name(sfont, G_TYPE_FROM_INSTANCE(item),
592                                           name, NULL);
593 
594     if(!name || strcmp(name, newname) != 0)
595     {
596         g_object_set(item, "name", newname, NULL);
597     }
598 
599     IPATCH_ITEM_WUNLOCK(sfont);
600 
601     g_free(name);
602     g_free(newname);
603 }
604 
605 /* base method to find an unused MIDI bank:program locale */
606 static void
ipatch_sf2_base_find_unused_locale(IpatchBase * base,int * bank,int * program,const IpatchItem * exclude,gboolean percussion)607 ipatch_sf2_base_find_unused_locale(IpatchBase *base, int *bank,
608                                    int *program, const IpatchItem *exclude,
609                                    gboolean percussion)
610 {
611     IpatchSF2 *sf = IPATCH_SF2(base);
612     GSList *locale_list = NULL;
613     IpatchSF2Preset *pset;
614     GSList *p;
615     guint b, n;			/* Stores current bank and program number */
616     guint lbank, lprogram;
617 
618     if(percussion)
619     {
620         *bank = 128;
621     }
622 
623     /* fill array with bank and program numbers */
624     IPATCH_ITEM_RLOCK(sf);
625     p = sf->presets;
626 
627     while(p)
628     {
629         pset = (IpatchSF2Preset *)(p->data);
630 
631         /* only add to locale list if not the exclude item */
632         if((gpointer)pset != (gpointer)exclude)
633             locale_list = g_slist_prepend(locale_list, GUINT_TO_POINTER
634                                           (((guint32)pset->bank << 16)
635                                            | pset->program));
636 
637         p = g_slist_next(p);
638     }
639 
640     IPATCH_ITEM_RUNLOCK(sf);
641 
642     if(!locale_list)
643     {
644         return;
645     }
646 
647     locale_list = g_slist_sort(locale_list, (GCompareFunc)locale_gcompare_func);
648 
649     b = *bank;
650     n = *program;
651 
652     /* loop through sorted list of bank:programs */
653     p = locale_list;
654 
655     while(p)
656     {
657         lprogram = GPOINTER_TO_UINT(p->data);
658         lbank = lprogram >> 16;
659         lprogram &= 0xFFFF;
660 
661         if(lbank > b || (lbank == b && lprogram > n))
662         {
663             break;
664         }
665 
666         if(lbank >= b)
667         {
668             if(++n > 127)
669             {
670                 n = 0;
671                 b++;
672             }
673         }
674 
675         p = g_slist_delete_link(p, p);  /* delete and advance */
676     }
677 
678     *bank = b;
679     *program = n;
680 
681     if(p)
682     {
683         g_slist_free(p);    /* free remainder of list */
684     }
685 }
686 
687 /* function used to do a temporary sort on preset list for
688    ipatch_sf2_base_find_unused_locale */
689 static int
locale_gcompare_func(gconstpointer a,gconstpointer b)690 locale_gcompare_func(gconstpointer a, gconstpointer b)
691 {
692     return (GPOINTER_TO_UINT(a) - GPOINTER_TO_UINT(b));
693 }
694 
695 static IpatchItem *
ipatch_sf2_base_find_item_by_locale(IpatchBase * base,int bank,int program)696 ipatch_sf2_base_find_item_by_locale(IpatchBase *base, int bank, int program)
697 {
698     IpatchSF2Preset *preset;
699 
700     preset = ipatch_sf2_find_preset(IPATCH_SF2(base), NULL, bank, program, NULL);
701     return ((IpatchItem *)preset);
702 }
703 
704 /**
705  * ipatch_sf2_new:
706  *
707  * Create a new SoundFont base object.
708  *
709  * Returns: New SoundFont base object with a reference count of 1. Caller
710  * owns the reference and removing it will destroy the item.
711  */
712 IpatchSF2 *
ipatch_sf2_new(void)713 ipatch_sf2_new(void)
714 {
715     return (IPATCH_SF2(g_object_new(IPATCH_TYPE_SF2, NULL)));
716 }
717 
718 /**
719  * ipatch_sf2_set_file:
720  * @sf: SoundFont to set file object of
721  * @file: File object to use with the SoundFont.
722  *
723  * Sets the file object of a SoundFont. SoundFont files are kept open
724  * for sample data that references the file. This function sets a
725  * SoundFonts authoritive file object. A convenience function, as
726  * ipatch_base_set_file() does the same thing (albeit without more specific
727  * type casting).
728  */
729 void
ipatch_sf2_set_file(IpatchSF2 * sf,IpatchSF2File * file)730 ipatch_sf2_set_file(IpatchSF2 *sf, IpatchSF2File *file)
731 {
732     g_return_if_fail(IPATCH_IS_SF2(sf));
733     g_return_if_fail(IPATCH_IS_SF2_FILE(file));
734 
735     ipatch_base_set_file(IPATCH_BASE(sf), IPATCH_FILE(file));
736 }
737 
738 /**
739  * ipatch_sf2_get_file:
740  * @sf: SoundFont to get file object of
741  *
742  * Gets the file object of a SoundFont. The returned SoundFont file object's
743  * reference count has incremented. The caller owns the reference and is
744  * responsible for removing it with <function>g_object_unref()</function>.
745  * A convenience function as ipatch_base_get_file() does the same thing
746  * (albeit without more specific type casting).
747  *
748  * Returns: (transfer full): The SoundFont file object or %NULL if @sf is not open. Remember
749  * to unref the file object with <function>g_object_unref()</function> when
750  * done with it.
751  */
752 IpatchSF2File *
ipatch_sf2_get_file(IpatchSF2 * sf)753 ipatch_sf2_get_file(IpatchSF2 *sf)
754 {
755     IpatchFile *file;
756 
757     g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL);
758 
759     file = ipatch_base_get_file(IPATCH_BASE(sf));
760 
761     if(file)
762     {
763         return (IPATCH_SF2_FILE(file));
764     }
765     else
766     {
767         return (NULL);
768     }
769 }
770 
771 /**
772  * ipatch_sf2_get_info:
773  * @sf: SoundFont to get info from
774  * @id: RIFF FOURCC id
775  *
776  * Get a SoundFont info string by RIFF FOURCC ID.
777  *
778  * Returns: New allocated info string value or %NULL if no info with the
779  * given @id. String should be freed when finished with it.
780  */
781 char *
ipatch_sf2_get_info(IpatchSF2 * sf,IpatchSF2InfoType id)782 ipatch_sf2_get_info(IpatchSF2 *sf, IpatchSF2InfoType id)
783 {
784     char *val;
785 
786     g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL);
787 
788     IPATCH_ITEM_RLOCK(sf);
789     val = g_hash_table_lookup(sf->info, GUINT_TO_POINTER(id));
790 
791     if(val)
792     {
793         val = g_strdup(val);
794     }
795 
796     IPATCH_ITEM_RUNLOCK(sf);
797 
798     return (val);
799 }
800 
801 /**
802  * ipatch_sf2_set_info:
803  * @sf: SoundFont to set info of
804  * @id: RIFF FOURCC id
805  * @val: Value to set info to or %NULL to unset (clear) info.
806  *
807  * Set SoundFont info.  Validates @id and ensures @val does not exceed
808  * the maximum allowed length for the given info type.
809  *
810  * Emits changed signal on SoundFont.
811  */
812 void
ipatch_sf2_set_info(IpatchSF2 * sf,IpatchSF2InfoType id,const char * val)813 ipatch_sf2_set_info(IpatchSF2 *sf, IpatchSF2InfoType id, const char *val)
814 {
815     GParamSpec *pspec;
816     GValue oldval = { 0 }, newval = { 0 };
817     int i;
818 
819     g_return_if_fail(IPATCH_IS_SF2(sf));
820 
821     for(i = 0; i < G_N_ELEMENTS(info_ids); i++)
822         if(info_ids[i] == id)
823         {
824             break;
825         }
826 
827     if(i == G_N_ELEMENTS(info_ids))
828     {
829         g_return_if_fail(ipatch_sf2_info_id_is_valid(id));    /* for debugging */
830         return;
831     }
832 
833     pspec = info_prop_pspecs[i];
834 
835     g_value_init(&oldval, G_TYPE_STRING);
836     g_value_take_string(&oldval, ipatch_sf2_get_info(sf, id));
837 
838     ipatch_sf2_real_set_info(sf, id, val);
839 
840     g_value_init(&newval, G_TYPE_STRING);
841     g_value_set_static_string(&newval, val);
842 
843     ipatch_item_prop_notify((IpatchItem *)sf, pspec, &newval, &oldval);
844 
845     /* need to do a title property notify? */
846     if(id == IPATCH_SF2_NAME)
847         ipatch_item_prop_notify((IpatchItem *)sf, ipatch_item_pspec_title,
848                                 &newval, &oldval);
849 
850     g_value_unset(&oldval);
851     g_value_unset(&newval);
852 }
853 
854 /* the real set info by id routine, user routine does a property notify */
855 static void
ipatch_sf2_real_set_info(IpatchSF2 * sf,IpatchSF2InfoType id,const char * val)856 ipatch_sf2_real_set_info(IpatchSF2 *sf, IpatchSF2InfoType id,
857                          const char *val)
858 {
859     char *newval = NULL;
860     guint maxlen;
861 
862     maxlen = ipatch_sf2_get_info_max_size(id);
863 
864     /* value exceeds max length? */
865     if(maxlen > 0 && val && strlen(val) > maxlen - 1)
866     {
867         g_warning("IpatchSF2Info string with id '%.4s' truncated",
868                   (char *)&id);
869         newval = g_strndup(val, maxlen - 1);
870     }
871     else if(val)
872     {
873         newval = g_strdup(val);
874     }
875 
876     /* we set up the hash table to free old string values */
877     IPATCH_ITEM_WLOCK(sf);
878 
879     if(newval)
880     {
881         g_hash_table_insert(sf->info, GINT_TO_POINTER(id), newval);
882     }
883     else
884     {
885         g_hash_table_remove(sf->info, GINT_TO_POINTER(id));
886     }
887 
888     IPATCH_ITEM_WUNLOCK(sf);
889 
890     /* newval has been taken over by hash table */
891 }
892 
893 /* a bag for ipatch_sf2_get_info_array() */
894 typedef struct
895 {
896     int count;
897     IpatchSF2Info *info;
898 } InfoArrayBag;
899 
900 /**
901  * ipatch_sf2_get_info_array: (skip)
902  * @sf: SoundFont to get all info strings from
903  *
904  * Get all string info (not IPATCH_SF2_VERSION or IPATCH_SF2_ROM_VERSION)
905  * from a SoundFont object. The array is sorted in the order recommended
906  * by the SoundFont standard for saving.
907  *
908  * Returns: A newly allocated array of info structures terminated by
909  * an array member with 0 valued <structfield>id</structfield>
910  * field. Remember to free the array with ipatch_sf2_free_info_array()
911  * when finished with it.
912  */
913 IpatchSF2Info *
ipatch_sf2_get_info_array(IpatchSF2 * sf)914 ipatch_sf2_get_info_array(IpatchSF2 *sf)
915 {
916     IpatchSF2Info *array;
917     InfoArrayBag bag;
918 
919     g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL);
920 
921     /* allocate max expected info elements + 1 for terminator */
922     array = g_malloc((IPATCH_SF2_INFO_COUNT + 1) * sizeof(IpatchSF2Info));
923 
924     bag.count = 0;
925     bag.info = array;
926 
927     IPATCH_ITEM_RLOCK(sf);
928     g_hash_table_foreach(sf->info, ipatch_sf2_foreach_info_GHFunc, &bag);
929     IPATCH_ITEM_RUNLOCK(sf);
930 
931     qsort(array, bag.count, sizeof(IpatchSF2Info),
932           ipatch_sf2_info_array_qsort);
933 
934     /* terminate array */
935     array[bag.count].id = 0;
936     array[bag.count].val = NULL;
937 
938     /* re-allocate for actual size */
939     return (g_realloc(array, (bag.count + 1) * sizeof(IpatchSF2Info)));
940 }
941 
942 static void
ipatch_sf2_foreach_info_GHFunc(gpointer key,gpointer value,gpointer data)943 ipatch_sf2_foreach_info_GHFunc(gpointer key, gpointer value, gpointer data)
944 {
945     InfoArrayBag *bag = (InfoArrayBag *)data;
946 
947     /* shouldn't happen, but just in case */
948     if(bag->count >= IPATCH_SF2_INFO_COUNT)
949     {
950         return;
951     }
952 
953     /* store the info ID and string in the info array */
954     bag->info[bag->count].id = GPOINTER_TO_UINT(key);
955     bag->info[bag->count].val = g_strdup((char *)value);
956     bag->count++;			/* advance to next index */
957 }
958 
959 /* sorts an info array according to recommended SoundFont order */
960 static int
ipatch_sf2_info_array_qsort(const void * a,const void * b)961 ipatch_sf2_info_array_qsort(const void *a, const void *b)
962 {
963     IpatchSF2Info *ainfo = (IpatchSF2Info *)a;
964     IpatchSF2Info *binfo = (IpatchSF2Info *)b;
965     int andx, bndx;
966 
967     /* find index in info ID array */
968     for(andx = 0; andx < G_N_ELEMENTS(info_ids); andx++)
969         if(info_ids[andx] == ainfo->id)
970         {
971             break;
972         }
973 
974     for(bndx = 0; bndx < G_N_ELEMENTS(info_ids); bndx++)
975         if(info_ids[bndx] == binfo->id)
976         {
977             break;
978         }
979 
980     return (andx - bndx);
981 }
982 
983 /**
984  * ipatch_sf2_free_info_array: (skip)
985  * @array: SoundFont info array
986  *
987  * Frees an info array returned by ipatch_sf2_get_info_array().
988  */
989 void
ipatch_sf2_free_info_array(IpatchSF2Info * array)990 ipatch_sf2_free_info_array(IpatchSF2Info *array)
991 {
992     int i;
993     g_return_if_fail(array != NULL);
994 
995     for(i = 0; array[i].id; i++)
996     {
997         g_free(array[i].val);
998     }
999 
1000     g_free(array);
1001 }
1002 
1003 /**
1004  * ipatch_sf2_info_id_is_valid: (skip)
1005  * @id: RIFF FOURCC id (see #IpatchSF2InfoType)
1006  *
1007  * Check if a given RIFF FOURCC id is a valid SoundFont info id.
1008  *
1009  * Returns: %TRUE if @id is a valid info id, %FALSE otherwise
1010  */
1011 gboolean
ipatch_sf2_info_id_is_valid(guint32 id)1012 ipatch_sf2_info_id_is_valid(guint32 id)
1013 {
1014     int i;
1015 
1016     for(i = 0; i < G_N_ELEMENTS(info_ids) ; i++)
1017         if(info_ids[i] == id)
1018         {
1019             return (TRUE);
1020         }
1021 
1022     return (FALSE);
1023 }
1024 
1025 /**
1026  * ipatch_sf2_get_info_max_size: (skip)
1027  * @infotype: An info enumeration
1028  *
1029  * Get maximum chunk size for info chunks.
1030  *
1031  * NOTE: Max size includes terminating NULL character so subtract one from
1032  * returned value to get max allowed string length.
1033  *
1034  * Returns: Maximum info chunk size or 0 if invalid @infotype. Subtract one
1035  * to get max allowed string length (if returned value was not 0).
1036  */
1037 int
ipatch_sf2_get_info_max_size(IpatchSF2InfoType infotype)1038 ipatch_sf2_get_info_max_size(IpatchSF2InfoType infotype)
1039 {
1040     if(!ipatch_sf2_info_id_is_valid(infotype))
1041     {
1042         return (0);
1043     }
1044 
1045     if(infotype == IPATCH_SF2_COMMENT)  /* comments can have up to 64k bytes */
1046     {
1047         return (65536);
1048     }
1049 
1050     if(infotype == IPATCH_SF2_VERSION  /* versions take up 4 bytes */
1051             || infotype == IPATCH_SF2_ROM_VERSION)
1052     {
1053         return (4);
1054     }
1055 
1056     return (256);	  /* all other valid info types allow 256 bytes max */
1057 }
1058 
1059 /**
1060  * ipatch_sf2_find_preset:
1061  * @sf: SoundFont to search in
1062  * @name: (nullable): Name of preset to find or %NULL to match any name
1063  * @bank: MIDI bank number of preset to search for or -1 to not search by
1064  *   MIDI bank:program numbers
1065  * @program: MIDI program number of preset to search for, only used
1066  *   if @bank is 0-128
1067  * @exclude: (nullable): A preset to exclude from the search or %NULL
1068  *
1069  * Find a preset by name or bank:preset MIDI numbers. If preset @name
1070  * and @bank:@program are specified then match for either condition.
1071  * If a preset is found its reference count is incremented before it
1072  * is returned. The caller is responsible for removing the reference
1073  * with g_object_unref() when finished with it.
1074  *
1075  * Returns: (transfer full): The matching preset or %NULL if not found. Remember to unref
1076  * the item when finished with it.
1077  */
1078 IpatchSF2Preset *
ipatch_sf2_find_preset(IpatchSF2 * sf,const char * name,int bank,int program,const IpatchSF2Preset * exclude)1079 ipatch_sf2_find_preset(IpatchSF2 *sf, const char *name, int bank,
1080                        int program, const IpatchSF2Preset *exclude)
1081 {
1082     IpatchSF2Preset *pset;
1083     gboolean bynum = FALSE;
1084     GSList *p;
1085 
1086     g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL);
1087 
1088     /* if bank and program are valid, then search by number */
1089     if(bank >= 0 && bank <= 128 && program >= 0 && program < 128)
1090     {
1091         bynum = TRUE;
1092     }
1093 
1094     IPATCH_ITEM_RLOCK(sf);
1095     p = sf->presets;
1096 
1097     while(p)
1098     {
1099         pset = (IpatchSF2Preset *)(p->data);
1100         IPATCH_ITEM_RLOCK(pset);	/* MT - Recursive LOCK */
1101 
1102         if(pset != exclude	/* if exclude is NULL it will never == pset */
1103                 && ((bynum && pset->bank == bank && pset->program == program)
1104                     || (name && strcmp(pset->name, name) == 0)))
1105         {
1106             g_object_ref(pset);
1107             IPATCH_ITEM_RUNLOCK(pset);
1108             IPATCH_ITEM_RUNLOCK(sf);
1109             return (pset);
1110         }
1111 
1112         IPATCH_ITEM_RUNLOCK(pset);
1113         p = g_slist_next(p);
1114     }
1115 
1116     IPATCH_ITEM_RUNLOCK(sf);
1117 
1118     return (NULL);
1119 }
1120 
1121 /**
1122  * ipatch_sf2_find_inst:
1123  * @sf: SoundFont to search in
1124  * @name: Name of Instrument to find
1125  * @exclude: (nullable): An instrument to exclude from the search or %NULL
1126  *
1127  * Find an instrument by @name in a SoundFont. If a matching instrument
1128  * is found, its reference count is incremented before it is returned.
1129  * The caller is responsible for removing the reference with g_object_unref()
1130  * when finished with it.
1131  *
1132  * Returns: (transfer full): The matching instrument or %NULL if not found. Remember to unref
1133  * the item when finished with it.
1134  */
1135 IpatchSF2Inst *
ipatch_sf2_find_inst(IpatchSF2 * sf,const char * name,const IpatchSF2Inst * exclude)1136 ipatch_sf2_find_inst(IpatchSF2 *sf, const char *name,
1137                      const IpatchSF2Inst *exclude)
1138 {
1139     IpatchSF2Inst *inst;
1140     GSList *p;
1141 
1142     g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL);
1143     g_return_val_if_fail(name != NULL, NULL);
1144 
1145     IPATCH_ITEM_RLOCK(sf);
1146     p = sf->insts;
1147 
1148     while(p)
1149     {
1150         inst = (IpatchSF2Inst *)(p->data);
1151         IPATCH_ITEM_RLOCK(inst);	/* MT - Recursive LOCK */
1152 
1153         if(inst != exclude && strcmp(inst->name, name) == 0)
1154         {
1155             g_object_ref(inst);
1156             IPATCH_ITEM_RUNLOCK(inst);
1157             IPATCH_ITEM_RUNLOCK(sf);
1158             return (inst);
1159         }
1160 
1161         IPATCH_ITEM_RUNLOCK(inst);
1162         p = g_slist_next(p);
1163     }
1164 
1165     IPATCH_ITEM_RUNLOCK(sf);
1166 
1167     return (NULL);
1168 }
1169 
1170 /**
1171  * ipatch_sf2_find_sample:
1172  * @sf: SoundFont to search in
1173  * @name: Name of sample to find
1174  * @exclude: (nullable): A sample to exclude from the search or %NULL
1175  *
1176  * Find a sample by @name in a SoundFont. If a sample is found its
1177  * reference count is incremented before it is returned. The caller
1178  * is responsible for removing the reference with g_object_unref()
1179  * when finished with it.
1180  *
1181  * Returns: (transfer full): The matching sample or %NULL if not found. Remember to unref
1182  * the item when finished with it.
1183  */
1184 IpatchSF2Sample *
ipatch_sf2_find_sample(IpatchSF2 * sf,const char * name,const IpatchSF2Sample * exclude)1185 ipatch_sf2_find_sample(IpatchSF2 *sf, const char *name,
1186                        const IpatchSF2Sample *exclude)
1187 {
1188     IpatchSF2Sample *sample;
1189     GSList *p;
1190 
1191     g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL);
1192     g_return_val_if_fail(name != NULL, NULL);
1193 
1194     IPATCH_ITEM_RLOCK(sf);
1195     p = sf->samples;
1196 
1197     while(p)
1198     {
1199         sample = (IpatchSF2Sample *)(p->data);
1200         IPATCH_ITEM_RLOCK(sample);  /* MT - Recursive LOCK */
1201 
1202         if(sample != exclude && strcmp(sample->name, name) == 0)
1203         {
1204             g_object_ref(sample);
1205             IPATCH_ITEM_RUNLOCK(sample);
1206             IPATCH_ITEM_RUNLOCK(sf);
1207             return (sample);
1208         }
1209 
1210         IPATCH_ITEM_RUNLOCK(sample);
1211         p = g_slist_next(p);
1212     }
1213 
1214     IPATCH_ITEM_RUNLOCK(sf);
1215 
1216     return (NULL);
1217 }
1218 
1219 /**
1220  * ipatch_sf2_get_zone_references:
1221  * @item: Item to locate referencing zones of, must be of type #IpatchSF2Inst
1222  *   or #IpatchSF2Sample and be parented to an #IpatchSF2 object.
1223  *
1224  * Get list of zones referencing an IpatchSF2Inst or IpatchSF2Sample.
1225  *
1226  * Returns: (transfer full): New object list containing #IpatchSF2Zone objects that
1227  *   refer to @item. The new list object has a reference count of 1
1228  *   which the caller owns, unreference to free the list.
1229  */
1230 IpatchList *
ipatch_sf2_get_zone_references(IpatchItem * item)1231 ipatch_sf2_get_zone_references(IpatchItem *item)
1232 {
1233     IpatchList *reflist, *itemlist, *zonelist;
1234     IpatchSF2 *sf;
1235     IpatchSF2Zone *zone;
1236     IpatchIter iter, zone_iter;
1237     IpatchItem *pitem;
1238 
1239     g_return_val_if_fail(IPATCH_IS_SF2_INST(item) || IPATCH_IS_SF2_SAMPLE(item), NULL);
1240 
1241     pitem = ipatch_item_get_parent(item);
1242     g_return_val_if_fail(IPATCH_IS_SF2(pitem), NULL);
1243     sf = IPATCH_SF2(pitem);
1244 
1245     /* ++ ref item list */
1246     if(IPATCH_IS_SF2_INST(item))   /* is an instrument? */
1247     {
1248         itemlist = ipatch_sf2_get_presets(sf);
1249     }
1250     else
1251     {
1252         itemlist = ipatch_sf2_get_insts(sf);    /* its a sample */
1253     }
1254 
1255     reflist = ipatch_list_new();	/* ++ ref new list */
1256 
1257     ipatch_list_init_iter(itemlist, &iter);
1258     pitem = ipatch_item_first(&iter);
1259 
1260     while(pitem)       /* loop on item list  */
1261     {
1262         /* ++ ref new zone list */
1263         zonelist = ipatch_container_get_children((IpatchContainer *)(pitem),
1264                    IPATCH_TYPE_SF2_ZONE);
1265         ipatch_list_init_iter(zonelist, &zone_iter);
1266 
1267         zone = ipatch_sf2_zone_first(&zone_iter);
1268 
1269         while(zone)
1270         {
1271             if(ipatch_sf2_zone_peek_link_item(zone) == item)
1272             {
1273                 g_object_ref(zone);  /* ++ ref zone for new list */
1274                 reflist->items = g_list_prepend(reflist->items, zone);
1275             }
1276 
1277             zone = ipatch_sf2_zone_next(&zone_iter);
1278         }
1279 
1280         g_object_unref(zonelist);  /* -- unref zone list */
1281         pitem = ipatch_item_next(&iter);
1282     }
1283 
1284     g_object_unref(itemlist);	/* -- unref item list */
1285 
1286     return (reflist);		/* !! caller takes over reference */
1287 }
1288 
1289 /* In theory there is still a chance of duplicates if another item's name is
1290    set to the generated unique one (by another thread) while in this routine */
1291 
1292 /**
1293  * ipatch_sf2_make_unique_name:
1294  * @sfont: SoundFont item
1295  * @child_type: A child type of @sfont to search for a unique name in
1296  * @name: (nullable): An initial name to use or %NULL
1297  * @exclude: (nullable): An item to exclude from search or %NULL
1298  *
1299  * Generates a unique name for the given @child_type in @sfont. The @name
1300  * parameter is used as a base and is modified, by appending a number, to
1301  * make it unique (if necessary). The @exclude parameter is used to exclude
1302  * an existing @sfont child item from the search.
1303  *
1304  * MT-Note: To ensure that an item is actually unique before being
1305  * added to a SoundFont object, ipatch_container_add_unique() should be
1306  * used.
1307  *
1308  * Returns: A new unique name which should be freed when finished with it.
1309  */
1310 char *
ipatch_sf2_make_unique_name(IpatchSF2 * sfont,GType child_type,const char * name,const IpatchItem * exclude)1311 ipatch_sf2_make_unique_name(IpatchSF2 *sfont, GType child_type,
1312                             const char *name, const IpatchItem *exclude)
1313 {
1314     GSList **list, *p;
1315     char curname[IPATCH_SFONT_NAME_SIZE + 1];
1316     int name_ofs;
1317     int count = 2;
1318 
1319     g_return_val_if_fail(IPATCH_IS_SF2(sfont), NULL);
1320 
1321     if(child_type == IPATCH_TYPE_SF2_PRESET)
1322     {
1323         list = &sfont->presets;
1324         name_ofs = G_STRUCT_OFFSET(IpatchSF2Preset, name);
1325 
1326         if(!name)
1327         {
1328             name = _("New Preset");
1329         }
1330     }
1331     else if(child_type == IPATCH_TYPE_SF2_INST)
1332     {
1333         list = &sfont->insts;
1334         name_ofs = G_STRUCT_OFFSET(IpatchSF2Inst, name);
1335 
1336         if(!name)
1337         {
1338             name = _("New Instrument");
1339         }
1340     }
1341     else if(child_type == IPATCH_TYPE_SF2_SAMPLE)
1342     {
1343         list = &sfont->samples;
1344         name_ofs = G_STRUCT_OFFSET(IpatchSF2Sample, name);
1345 
1346         if(!name)
1347         {
1348             name = _("New Sample");
1349         }
1350     }
1351     else
1352     {
1353         g_critical(IPATCH_CONTAINER_ERRMSG_INVALID_CHILD_2,
1354                    g_type_name(child_type), g_type_name(IPATCH_TYPE_SF2));
1355         return (NULL);
1356     }
1357 
1358     g_strlcpy(curname, name, sizeof(curname));
1359 
1360     IPATCH_ITEM_RLOCK(sfont);
1361 
1362     p = *list;
1363 
1364     while(p)	/* check for duplicate */
1365     {
1366         IPATCH_ITEM_RLOCK(p->data);  /* MT - Recursive LOCK */
1367 
1368         if(p->data != exclude
1369                 && strcmp(G_STRUCT_MEMBER(char *, p->data, name_ofs),
1370                           curname) == 0)
1371         {
1372             /* duplicate name */
1373             IPATCH_ITEM_RUNLOCK(p->data);
1374 
1375             ipatch_strconcat_num(name, count++, curname, sizeof(curname));
1376 
1377             p = *list;		/* start over */
1378             continue;
1379         }
1380 
1381         IPATCH_ITEM_RUNLOCK(p->data);
1382         p = g_slist_next(p);
1383     }
1384 
1385     IPATCH_ITEM_RUNLOCK(sfont);
1386 
1387     return (g_strdup(curname));
1388 }
1389