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: misc
22  * @short_description: Miscellaneous stuff
23  * @see_also:
24  * @stability: Stable
25  */
26 #if defined(HAVE_CONFIG_H)
27 #include "config.h"
28 #endif
29 
30 #include <glib.h>
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <errno.h>
35 
36 /* for mkdir */
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 
40 #include "libinstpatch.h"
41 #include "ipatch_priv.h"
42 #include "i18n.h"
43 
44 /* private initializers in other source files */
45 void _ipatch_sf2_gen_init(void);  /* IpatchSF2Gen.c */
46 void _ipatch_param_init(void);	/* IpatchParam.c */
47 void _ipatch_type_prop_init(void);  /* IpatchTypeProp.c */
48 void _ipatch_util_init(void);	/* util.c */
49 void _ipatch_unit_init(void);	/* unit.c */
50 void _ipatch_xml_object_init(void);	/* IpatchXmlObject.c */
51 void _ipatch_range_init(void);	/* IpatchRange.c */
52 
53 void _ipatch_convert_SF2_init(void);
54 void _ipatch_convert_gig_init(void);
55 void _ipatch_convert_DLS2_init(void);
56 void _ipatch_convert_SLI_init(void);
57 void _ipatch_sf2_voice_cache_init_DLS(void);
58 void _ipatch_sf2_voice_cache_init_SF2(void);
59 void _ipatch_sf2_voice_cache_init_SLI(void);
60 void _ipatch_sf2_voice_cache_init_gig(void);
61 void _ipatch_sf2_voice_cache_init_VBank(void);
62 void _ipatch_container_notify_init(void);
63 void _ipatch_DLS2_infos_init(void);
64 void _ipatch_DLS2_sample_init(void);
65 void _ipatch_file_init(void);
66 void _ipatch_item_init(void);
67 void _ipatch_sample_data_init(void);
68 void _ipatch_sample_store_swap_recover_init(void);
69 void _ipatch_converter_init(void);
70 void _ipatch_sample_transform_init();
71 void _ipatch_sf2_mod_list_init();
72 void _ipatch_paste_init(void);
73 
74 /* private free functions in other source files */
75 void _ipatch_param_deinit(void);
76 void _ipatch_type_prop_deinit(void);
77 void _ipatch_unit_deinit(void);
78 void _ipatch_xml_object_deinit(void);
79 void _ipatch_util_deinit(void);
80 void _ipatch_sf2_gen_deinit(void);
81 void _ipatch_container_notify_deinit(void);
82 void _ipatch_DLS2_infos_deinit(void);
83 void _ipatch_DLS2_sample_deinit(void);
84 void _ipatch_file_deinit(void);
85 void _ipatch_item_deinit(void);
86 void _ipatch_sample_data_deinit(void);
87 void _ipatch_sample_store_swap_recover_deinit(void);
88 void _ipatch_converter_deinit(void);
89 void _ipatch_sample_transform_deinit(void);
90 void _ipatch_sf2_mod_list_deinit(void);
91 void _ipatch_paste_deinit(void);
92 
93 static gboolean ipatch_strv_xml_encode(GNode *node, GObject *object,
94                                        GParamSpec *pspec, GValue *value,
95                                        GError **err);
96 static gboolean ipatch_strv_xml_decode(GNode *node, GObject *object,
97                                        GParamSpec *pspec, GValue *value,
98                                        GError **err);
99 static void virtual_parent_dls2_inst(GType type, GParamSpec *spec,
100                                      GValue *value, GObject *object);
101 static void virtual_parent_gig_inst(GType type, GParamSpec *spec,
102                                     GValue *value, GObject *object);
103 static void virtual_parent_sf2_preset(GType type, GParamSpec *spec,
104                                       GValue *value, GObject *object);
105 static void virtual_parent_sf2_sample(GType type, GParamSpec *spec,
106                                       GValue *value, GObject *object);
107 static void conform_percussion(GObject *object);
108 static void conform_melodic(GObject *object);
109 
110 static void dump_recursive(GObject *object, char *indent, FILE *file);
111 static void dump_object_info(GObject *object, char *indent, FILE *file);
112 
113 typedef struct
114 {
115     char *type_name;
116     char *name;
117     char *blurb;
118     int category;
119 } TypePropInit;
120 
121 /* info to initialize type properties */
122 static TypePropInit type_props[] =
123 {
124     { "IpatchSampleStoreSndFile", N_("Sample file"), NULL, IPATCH_CATEGORY_SAMPLE },
125     { "IpatchDLS2", N_("DLS"), N_("Down Loadable Sounds"), IPATCH_CATEGORY_BASE },
126     { "IpatchDLS2Inst", N_("Instrument"), N_("DLS Instrument"), IPATCH_CATEGORY_PROGRAM },
127     { "IpatchDLS2Region", N_("Region"), N_("DLS Region"), IPATCH_CATEGORY_SAMPLE_REF },
128     { "IpatchDLS2Sample", N_("Sample"), N_("DLS Sample"), IPATCH_CATEGORY_SAMPLE },
129     { "IpatchGig", N_("GigaSampler"), NULL, IPATCH_CATEGORY_BASE },
130     { "IpatchGigDimension", N_("Dimension"), N_("GigaSampler Dimension"), IPATCH_CATEGORY_NONE },
131     { "IpatchGigInst", N_("Instrument"), N_("GigaSampler Instrument"), IPATCH_CATEGORY_PROGRAM },
132     { "IpatchGigRegion", N_("Region"), N_("GigaSampler Region"), IPATCH_CATEGORY_NONE },
133     { "IpatchGigSample", N_("Sample"), N_("GigaSampler Sample"), IPATCH_CATEGORY_SAMPLE },
134     { "IpatchGigSubRegion", N_("Sub Region"), N_("GigaSampler Sub Region"), IPATCH_CATEGORY_SAMPLE_REF },
135     { "IpatchSF2", N_("SoundFont"), NULL, IPATCH_CATEGORY_BASE },
136     { "IpatchSF2Inst", N_("Instrument"), N_("SoundFont Instrument"), IPATCH_CATEGORY_INSTRUMENT },
137     { "IpatchSF2IZone", N_("Zone"), N_("SoundFont Instrument Zone"), IPATCH_CATEGORY_SAMPLE_REF },
138     { "IpatchSF2Preset", N_("Preset"), N_("SoundFont Preset"), IPATCH_CATEGORY_PROGRAM },
139     { "IpatchSF2PZone", N_("Zone"), N_("SoundFont Preset Zone"), IPATCH_CATEGORY_INSTRUMENT_REF },
140     { "IpatchSF2Sample", N_("Sample"), N_("SoundFont Sample"), IPATCH_CATEGORY_SAMPLE },
141     { "IpatchSLI", N_("Spectralis"), NULL, IPATCH_CATEGORY_BASE },
142     { "IpatchSLIInst", N_("Instrument"), N_("Spectralis Instrument"), IPATCH_CATEGORY_INSTRUMENT },
143     { "IpatchSLIZone", N_("Zone"), N_("Spectralis Instrument Zone"), IPATCH_CATEGORY_SAMPLE_REF },
144     { "IpatchSLISample", N_("Sample"), N_("Spectralis Sample"), IPATCH_CATEGORY_SAMPLE },
145     { "IpatchVBank", N_("VBank"), N_("Virtual Bank"), IPATCH_CATEGORY_BASE },
146     { "IpatchVBankInst", N_("Instrument"), N_("VBank Instrument"), IPATCH_CATEGORY_PROGRAM },
147     { "IpatchVBankRegion", N_("Region"), N_("VBank Region"), IPATCH_CATEGORY_INSTRUMENT_REF }
148 };
149 
150 /* name of application using libInstPatch (for saving to files) */
151 char *ipatch_application_name = NULL;
152 
153 G_LOCK_DEFINE_STATIC(lock_init);
154 static int init_counter = 0;
155 
156 /*-----------------------------------------------------------------------------
157  Initialization / deinitialization of libinstpatch library.
158  Any application should call ipatch_init() once before any other libinstpatch
159  functions.
160  When the application is complete it must call ipatch_close().
161 
162  For multi task application it is best that only one task be responsible of
163  initialization/closing. Typically, the main task of the application should
164  call ipatch_init() before creating other tasks, then when the application is
165  complete, the main task should call ipatch_close() after the other tasks are
166  completed.
167 
168  If the application make use of multiple libraries each dependent of libinstpatch,
169  for each call to ipatch_init() these libraries should call ipatch_close().
170 -----------------------------------------------------------------------------*/
171 
172 /**
173  * ipatch_init:
174  *
175  * Initialize libInstPatch library. Should be called before any other
176  * libInstPatch related functions.
177  */
178 void
ipatch_init(void)179 ipatch_init(void)
180 {
181     TypePropInit *prop_info;
182     GType type;
183     int i;
184 
185     /* do nothing if the library is already initialized */
186     G_LOCK(lock_init);
187     init_counter++;
188     if(init_counter > 1)
189     {
190         /* library already initialized */
191         G_UNLOCK(lock_init);
192         return;
193     }
194 
195     g_type_init();
196 
197     if(!g_thread_supported())
198     {
199         g_thread_init(NULL);
200     }
201 
202     /* bind the gettext domain */
203 #if defined(ENABLE_NLS)
204     bindtextdomain(PACKAGE, LOCALEDIR);
205 #endif
206 
207     /* Must be done before other types since they may be dependent */
208 
209     /* Initialize 'GParamSpec extended properties' system */
210     _ipatch_param_init();
211 
212     /* Initialize the 'GObject style properties' system for GTypes */
213     _ipatch_type_prop_init();
214 
215     /* Initialize 'unit conversion' system */
216     _ipatch_unit_init();
217 
218     /* Initialize object's properties 'encoding/decoding XML handlers' system */
219     _ipatch_xml_object_init();
220 
221     /* Initialize GValue constant values */
222     _ipatch_util_init();
223 
224     /* Initialize 'SoundFont generators' subsystem */
225     _ipatch_sf2_gen_init();
226 
227     /*------------------------------------------------------------------------
228      Initialize object subsystem (list, hash) before objects type.
229      These list or hash are specfific to the respective object.
230      Initialization/free functions are in the respective object module file
231      Here initialization function _xxx_init() are called.
232      Respective function _xxx_deinit() are called in ipatch_deinit().
233     -------------------------------------------------------------------------*/
234     _ipatch_container_notify_init();
235     _ipatch_DLS2_infos_init();
236     _ipatch_DLS2_sample_init();
237     _ipatch_file_init();
238     _ipatch_item_init();
239     _ipatch_sample_data_init();
240     _ipatch_sample_store_swap_recover_init();
241     _ipatch_converter_init();
242     _ipatch_sample_transform_init();
243     _ipatch_sf2_mod_list_init();
244     _ipatch_paste_init();
245 
246     /*-------------------------------------------------------------------------
247      initialize interfaces type before objects
248     --------------------------------------------------------------------------*/
249     /* initialize interfaces before objects */
250     ipatch_sample_get_type();
251     ipatch_sf2_gen_item_get_type();
252     ipatch_sf2_mod_item_get_type();
253 
254     /* declares property types which other types may use */
255     g_type_class_ref(IPATCH_TYPE_SF2_VOICE_CACHE);
256 
257 
258     g_type_class_ref(IPATCH_TYPE_BASE);
259     g_type_class_ref(IPATCH_TYPE_CONTAINER);
260     g_type_class_ref(IPATCH_TYPE_CONVERTER);
261     g_type_class_ref(IPATCH_TYPE_DLS2);
262     ipatch_dls2_conn_get_type();
263     g_type_class_ref(IPATCH_TYPE_DLS2_INST);
264     g_type_class_ref(IPATCH_TYPE_DLS2_REGION);
265     g_type_class_ref(IPATCH_TYPE_DLS2_SAMPLE);
266     g_type_class_ref(IPATCH_TYPE_DLS_FILE);
267     g_type_class_ref(IPATCH_TYPE_DLS_READER);
268     g_type_class_ref(IPATCH_TYPE_DLS_WRITER);
269     g_type_class_ref(IPATCH_TYPE_FILE);
270     ipatch_file_handle_get_type();
271     g_type_class_ref(IPATCH_TYPE_GIG_FILE);
272     g_type_class_ref(IPATCH_TYPE_GIG);
273     g_type_class_ref(IPATCH_TYPE_GIG_DIMENSION);
274     g_type_class_ref(IPATCH_TYPE_GIG_INST);
275     g_type_class_ref(IPATCH_TYPE_GIG_REGION);
276     g_type_class_ref(IPATCH_TYPE_GIG_SAMPLE);
277     g_type_class_ref(IPATCH_TYPE_GIG_SUB_REGION);
278     g_type_class_ref(IPATCH_TYPE_ITEM);
279     ipatch_iter_get_type();
280     g_type_class_ref(IPATCH_TYPE_LIST);
281     ipatch_param_spec_range_get_type();
282     g_type_class_ref(IPATCH_TYPE_PASTE);
283     ipatch_range_get_type();
284     g_type_class_ref(IPATCH_TYPE_RIFF);
285     g_type_class_ref(IPATCH_TYPE_SAMPLE_DATA);
286     g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE);
287     g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_FILE);
288     g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_RAM);
289     g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_ROM);
290     g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_SND_FILE);
291     g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_SPLIT24);
292     g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_SWAP);
293     g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_VIRTUAL);
294     g_type_class_ref(IPATCH_TYPE_SF2_FILE);
295     ipatch_sf2_gen_array_get_type();
296     g_type_class_ref(IPATCH_TYPE_SF2);
297     g_type_class_ref(IPATCH_TYPE_SF2_INST);
298     g_type_class_ref(IPATCH_TYPE_SF2_IZONE);
299     g_type_class_ref(IPATCH_TYPE_SF2_READER);
300     ipatch_sf2_mod_get_type();
301     ipatch_sf2_mod_list_get_type();
302     ipatch_sample_transform_get_type();
303     ipatch_sample_list_get_type();
304     ipatch_sample_list_item_get_type();
305     g_type_class_ref(IPATCH_TYPE_SF2_PRESET);
306     g_type_class_ref(IPATCH_TYPE_SF2_PZONE);
307     g_type_class_ref(IPATCH_TYPE_SF2_SAMPLE);
308     g_type_class_ref(IPATCH_TYPE_SLI_FILE);
309     g_type_class_ref(IPATCH_TYPE_SLI);
310     g_type_class_ref(IPATCH_TYPE_SLI_INST);
311     g_type_class_ref(IPATCH_TYPE_SLI_ZONE);
312     g_type_class_ref(IPATCH_TYPE_SLI_SAMPLE);
313     g_type_class_ref(IPATCH_TYPE_SLI_READER);
314     g_type_class_ref(IPATCH_TYPE_VBANK);
315     g_type_class_ref(IPATCH_TYPE_VBANK_INST);
316     g_type_class_ref(IPATCH_TYPE_VBANK_REGION);
317     g_type_class_ref(IPATCH_TYPE_SF2_WRITER);
318     g_type_class_ref(IPATCH_TYPE_SF2_ZONE);
319     g_type_class_ref(IPATCH_TYPE_SND_FILE);
320 
321     _ipatch_convert_SF2_init();
322     _ipatch_convert_gig_init();
323     _ipatch_convert_DLS2_init();
324     _ipatch_convert_SLI_init();
325 
326     _ipatch_sf2_voice_cache_init_DLS();
327     _ipatch_sf2_voice_cache_init_SF2();
328     _ipatch_sf2_voice_cache_init_SLI();
329     _ipatch_sf2_voice_cache_init_gig();
330     _ipatch_sf2_voice_cache_init_VBank();
331 
332     _ipatch_range_init();
333 
334     /* Register XML encode/decode handlers */
335 
336     /* GLib string array boxed type encode/decode */
337     ipatch_xml_register_handler(G_TYPE_STRV, NULL, ipatch_strv_xml_encode,
338                                 ipatch_strv_xml_decode);
339 
340     /* set type properties */
341 
342     for(i = 0; i < G_N_ELEMENTS(type_props); i++)
343     {
344         type = g_type_from_name(type_props[i].type_name);
345 
346         if(log_if_fail(type != 0))
347         {
348             continue;
349         }
350 
351         prop_info = &type_props[i];
352 
353         if(prop_info->name)
354         {
355             ipatch_type_set(type, "name", prop_info->name, NULL);
356         }
357 
358         if(prop_info->blurb)
359         {
360             ipatch_type_set(type, "blurb", prop_info->blurb, NULL);
361         }
362 
363         if(prop_info->category != IPATCH_CATEGORY_NONE)
364         {
365             ipatch_type_set(type, "category", prop_info->category, NULL);
366         }
367     }
368 
369     /* link types */
370 
371     ipatch_type_set(IPATCH_TYPE_DLS2_REGION, "link-type",
372                     IPATCH_TYPE_DLS2_SAMPLE, NULL);
373 
374     ipatch_type_set(IPATCH_TYPE_GIG_SUB_REGION, "link-type",
375                     IPATCH_TYPE_GIG_SAMPLE, NULL);
376 
377     ipatch_type_set(IPATCH_TYPE_SF2_PZONE, "link-type",
378                     IPATCH_TYPE_SF2_INST, NULL);
379 
380     ipatch_type_set(IPATCH_TYPE_SF2_IZONE, "link-type",
381                     IPATCH_TYPE_SF2_SAMPLE, NULL);
382 
383     ipatch_type_set(IPATCH_TYPE_SLI_ZONE, "link-type",
384                     IPATCH_TYPE_SLI_SAMPLE, NULL);
385 
386     ipatch_type_set(IPATCH_TYPE_VBANK_REGION, "link-type",
387                     IPATCH_TYPE_ITEM, NULL);
388 
389     /* virtual container parent type properties */
390 
391     ipatch_type_set(IPATCH_TYPE_DLS2_SAMPLE,
392                     "virtual-parent-type", IPATCH_TYPE_VIRTUAL_DLS2_SAMPLES,
393                     NULL);
394     ipatch_type_set(IPATCH_TYPE_GIG_SAMPLE,
395                     "virtual-parent-type", IPATCH_TYPE_VIRTUAL_GIG_SAMPLES,
396                     NULL);
397     ipatch_type_set(IPATCH_TYPE_SF2_INST,
398                     "virtual-parent-type", IPATCH_TYPE_VIRTUAL_SF2_INST,
399                     NULL);
400     ipatch_type_set(IPATCH_TYPE_SLI_INST,
401                     "virtual-parent-type", IPATCH_TYPE_VIRTUAL_SLI_INST,
402                     NULL);
403     ipatch_type_set(IPATCH_TYPE_SLI_SAMPLE,
404                     "virtual-parent-type", IPATCH_TYPE_VIRTUAL_SLI_SAMPLES,
405                     NULL);
406 
407     /* dynamic virtual container properties (determined by object instance) */
408     ipatch_type_set_dynamic_func(IPATCH_TYPE_DLS2_INST, "virtual-parent-type",
409                                  virtual_parent_dls2_inst);
410     ipatch_type_set_dynamic_func(IPATCH_TYPE_GIG_INST, "virtual-parent-type",
411                                  virtual_parent_gig_inst);
412     ipatch_type_set_dynamic_func(IPATCH_TYPE_SF2_PRESET, "virtual-parent-type",
413                                  virtual_parent_sf2_preset);
414     ipatch_type_set_dynamic_func(IPATCH_TYPE_SF2_SAMPLE, "virtual-parent-type",
415                                  virtual_parent_sf2_sample);
416 
417     /* child object conform functions (for making a child object conform to a
418      * specific virtual container) */
419     ipatch_type_set(IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION,
420                     "virtual-child-conform-func", conform_percussion,
421                     NULL);
422     ipatch_type_set(IPATCH_TYPE_VIRTUAL_DLS2_MELODIC,
423                     "virtual-child-conform-func", conform_melodic,
424                     NULL);
425     ipatch_type_set(IPATCH_TYPE_VIRTUAL_GIG_PERCUSSION,
426                     "virtual-child-conform-func", conform_percussion,
427                     NULL);
428     ipatch_type_set(IPATCH_TYPE_VIRTUAL_GIG_MELODIC,
429                     "virtual-child-conform-func", conform_melodic,
430                     NULL);
431     ipatch_type_set(IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION,
432                     "virtual-child-conform-func", conform_percussion,
433                     NULL);
434     ipatch_type_set(IPATCH_TYPE_VIRTUAL_SF2_MELODIC,
435                     "virtual-child-conform-func", conform_melodic,
436                     NULL);
437 
438     /* container child sorting */
439     ipatch_type_set(IPATCH_TYPE_VIRTUAL_DLS2_MELODIC,
440                     "sort-children", TRUE, NULL);
441     ipatch_type_set(IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION,
442                     "sort-children", TRUE, NULL);
443 
444     ipatch_type_set(IPATCH_TYPE_VIRTUAL_GIG_MELODIC,
445                     "sort-children", TRUE, NULL);
446     ipatch_type_set(IPATCH_TYPE_VIRTUAL_GIG_PERCUSSION,
447                     "sort-children", TRUE, NULL);
448 
449     ipatch_type_set(IPATCH_TYPE_VIRTUAL_SF2_MELODIC,
450                     "sort-children", TRUE, NULL);
451     ipatch_type_set(IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION,
452                     "sort-children", TRUE, NULL);
453 
454     ipatch_type_set(IPATCH_TYPE_VBANK,
455                     "sort-children", TRUE, NULL);
456 
457     /* set "splits-type" properties */
458     ipatch_type_set(IPATCH_TYPE_SF2_PRESET,
459                     "splits-type", IPATCH_SPLITS_NORMAL, NULL);
460     ipatch_type_set(IPATCH_TYPE_SF2_INST,
461                     "splits-type", IPATCH_SPLITS_NORMAL, NULL);
462     ipatch_type_set(IPATCH_TYPE_DLS2_INST,
463                     "splits-type", IPATCH_SPLITS_NORMAL, NULL);
464     ipatch_type_set(IPATCH_TYPE_GIG_INST,
465                     "splits-type", IPATCH_SPLITS_NO_OVERLAP, NULL);
466     ipatch_type_set(IPATCH_TYPE_SLI_INST,
467                     "splits-type", IPATCH_SPLITS_NORMAL, NULL);
468     ipatch_type_set(IPATCH_TYPE_VBANK_INST,
469                     "splits-type", IPATCH_SPLITS_NORMAL, NULL);
470 
471     /* set "mime-type" properties */
472     ipatch_type_set(IPATCH_TYPE_SF2_FILE,
473                     "mime-type", "audio/x-soundfont", NULL);
474     ipatch_type_set(IPATCH_TYPE_DLS_FILE,
475                     "mime-type", "audio/dls", NULL);
476     ipatch_type_set(IPATCH_TYPE_GIG_FILE,
477                     "mime-type", "audio/x-gigasampler", NULL);
478     ipatch_type_set(IPATCH_TYPE_SLI_FILE,
479                     "mime-type", "audio/x-spectralis", NULL);
480 
481     G_UNLOCK(lock_init);
482 }
483 
484 /**
485  * ipatch_deinit:
486  *
487  * Free libInstPatch library. Should be called when the application have
488  * finished.
489  */
490 static void
ipatch_deinit(void)491 ipatch_deinit(void)
492 {
493     g_free(ipatch_application_name);
494 
495     /*-------------------------------------------------------------------------
496       Free internal systems
497     -------------------------------------------------------------------------*/
498     /* Free 'GParamSpec extended properties' system */
499     _ipatch_param_deinit();
500 
501     /* Free the 'GObject style properties' system for GTypes */
502     _ipatch_type_prop_deinit();
503 
504     /* Free 'unit conversion' system */
505     _ipatch_unit_deinit();
506 
507     /* Free object's properties 'encoding/decoding XML handlers' system */
508     _ipatch_xml_object_deinit();
509 
510     /* Free GValue constant values */
511     _ipatch_util_deinit();
512 
513     /* Free 'SoundFont generators' subsystem */
514     _ipatch_sf2_gen_deinit();
515 
516     /*-------------------------------------------------------------------------
517      Free object subsystem (list, hash).
518      These list or hash are specfific to the respective object.
519      Initialization/deinitialization functions are in the respective object
520      module file.
521      Here deinitialization functions  _xxx_deinit() are called.
522      Respective initialization functions _xxx_init() are called in
523      ipatch_init().
524     -------------------------------------------------------------------------*/
525     /* Free container subsystem */
526     _ipatch_container_notify_deinit();
527 
528     /* Free DLS2 subsystem */
529     _ipatch_DLS2_infos_deinit();
530 
531     /* Free DLS2 sample subsystem */
532     _ipatch_DLS2_sample_deinit();
533 
534     /* Free File subsystem */
535     _ipatch_file_deinit();
536 
537     /* Free Item propterty subsystem */
538     _ipatch_item_deinit();
539 
540     /* Free Sample data subsystem */
541     _ipatch_sample_data_deinit();
542 
543     /* Free Sample store swap recovery subsystem */
544     _ipatch_sample_store_swap_recover_deinit();
545 
546     /* Free converter subsytem */
547     _ipatch_converter_deinit();
548 
549     /* Free Audio format conversion subsystem */
550     _ipatch_sample_transform_deinit();
551 
552     /* Free default mod list */
553     _ipatch_sf2_mod_list_deinit();
554 
555     /* Free paste handlers list */
556     _ipatch_paste_deinit();
557 }
558 
559 /**
560  * ipatch_close:
561  *
562  * This should be called prior to application close.
563  *
564  * Decrement the reference counter and if it reaches 0 performs cleanup of
565  * libInstPatch, such as deleting temporary files and internal caches.
566  * If the counter is still > 0, the function return without doing cleanup
567  * (the library is still owned).
568  *
569  * Does nothing if the library is already deinitialized (or was not initialized).
570  * Since: 1.1.0
571  */
572 void
ipatch_close(void)573 ipatch_close(void)
574 {
575     /* do nothing if the library is already deinitialized */
576     G_LOCK(lock_init);
577     init_counter--;
578     if(init_counter != 0)
579     {
580         /* library still owned by a task, do nothing */
581         if(init_counter < 0)
582         {
583             init_counter = 0;
584         }
585         G_UNLOCK(lock_init);
586         return;
587     }
588 
589     ipatch_sample_store_swap_close();
590     ipatch_deinit();
591 
592     G_UNLOCK(lock_init);
593 }
594 
595 static gboolean
ipatch_strv_xml_encode(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)596 ipatch_strv_xml_encode(GNode *node, GObject *object, GParamSpec *pspec,
597                        GValue *value, GError **err)
598 {
599     GStrv strv;
600 
601     g_return_val_if_fail(G_VALUE_HOLDS(value, G_TYPE_STRV), FALSE);
602 
603     strv = g_value_get_boxed(value);
604 
605     if(!strv)
606     {
607         ipatch_xml_set_attribute(node, "null", "1");
608         return (TRUE);
609     }
610 
611     for(; *strv; strv++)
612     {
613         ipatch_xml_new_node(node, "value", *strv, NULL);
614     }
615 
616     return (TRUE);
617 }
618 
619 static gboolean
ipatch_strv_xml_decode(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)620 ipatch_strv_xml_decode(GNode *node, GObject *object, GParamSpec *pspec,
621                        GValue *value, GError **err)
622 {
623     GStrv strv;
624     GNode *n;
625     int i;
626 
627     g_return_val_if_fail(G_VALUE_HOLDS(value, G_TYPE_STRV), FALSE);
628 
629     if(ipatch_xml_test_attribute(node, "null", "1"))
630     {
631         g_value_set_boxed(value, NULL);
632         return (TRUE);
633     }
634 
635     /* Count "value" child nodes */
636     for(i = 0, n = node->children; n; n = n->next)
637         if(ipatch_xml_test_name(n, "value"))
638         {
639             i++;
640         }
641 
642     strv = g_new(char *, i + 1);		/* ++ alloc new strv array */
643 
644     for(i = 0, n = node->children; n; n = n->next)
645     {
646         if(!ipatch_xml_test_name(n, "value"))
647         {
648             continue;
649         }
650 
651         strv[i] = ipatch_xml_dup_value(n);
652         i++;
653     }
654 
655     strv[i] = NULL;
656 
657     g_value_take_boxed(value, strv);
658 
659     return (TRUE);
660 }
661 
662 static void
virtual_parent_dls2_inst(GType type,GParamSpec * spec,GValue * value,GObject * object)663 virtual_parent_dls2_inst(GType type, GParamSpec *spec, GValue *value,
664                          GObject *object)
665 {
666     gboolean percuss = FALSE;
667 
668     if(object)
669     {
670         g_object_get(object, "percussion", &percuss, NULL);
671     }
672 
673     if(percuss)
674     {
675         g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION);
676     }
677     else
678     {
679         g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_DLS2_MELODIC);
680     }
681 }
682 
683 static void
virtual_parent_gig_inst(GType type,GParamSpec * spec,GValue * value,GObject * object)684 virtual_parent_gig_inst(GType type, GParamSpec *spec, GValue *value,
685                         GObject *object)
686 {
687     gboolean percuss = FALSE;
688 
689     if(object)
690     {
691         g_object_get(object, "percussion", &percuss, NULL);
692     }
693 
694     if(percuss)
695     {
696         g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_GIG_PERCUSSION);
697     }
698     else
699     {
700         g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_GIG_MELODIC);
701     }
702 }
703 
704 static void
virtual_parent_sf2_preset(GType type,GParamSpec * spec,GValue * value,GObject * object)705 virtual_parent_sf2_preset(GType type, GParamSpec *spec, GValue *value,
706                           GObject *object)
707 {
708     gboolean percuss = FALSE;
709 
710     if(object)
711     {
712         g_object_get(object, "percussion", &percuss, NULL);
713     }
714 
715     if(percuss)
716     {
717         g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION);
718     }
719     else
720     {
721         g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_SF2_MELODIC);
722     }
723 }
724 
725 static void
virtual_parent_sf2_sample(GType type,GParamSpec * spec,GValue * value,GObject * object)726 virtual_parent_sf2_sample(GType type, GParamSpec *spec, GValue *value,
727                           GObject *object)
728 {
729     gboolean rom = FALSE;
730 
731     if(object)
732     {
733         g_object_get(object, "rom", &rom, NULL);
734     }
735 
736     if(rom)
737     {
738         g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_SF2_ROM);
739     }
740     else
741     {
742         g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_SF2_SAMPLES);
743     }
744 }
745 
746 static void
conform_percussion(GObject * object)747 conform_percussion(GObject *object)
748 {
749     g_object_set(object, "percussion", TRUE, NULL);
750 }
751 
752 static void
conform_melodic(GObject * object)753 conform_melodic(GObject *object)
754 {
755     g_object_set(object, "percussion", FALSE, NULL);
756 }
757 
758 
759 /**
760  * ipatch_set_application_name:
761  * @name: Application name and version (example: "swami 1.0") or %NULL to
762  *   unset application name
763  *
764  * Set the global application name string which is used as the
765  * software string written to patch files. This string should contain
766  * the name of the application, and its version, that is using
767  * libInstPatch. The libInstPatch version will also be output where
768  * appropriate, so the software string written to a SoundFont for
769  * example would look something like "swami 1.0 (libInstPatch 1.0)".
770  */
771 void
ipatch_set_application_name(const char * name)772 ipatch_set_application_name(const char *name)
773 {
774     if(ipatch_application_name)
775     {
776         g_free(ipatch_application_name);
777     }
778 
779     if(name)
780     {
781         ipatch_application_name = g_strdup(name);
782     }
783     else
784     {
785         ipatch_application_name = NULL;
786     }
787 }
788 
789 /**
790  * ipatch_version:
791  * @major: (out) (optional): Pointer to store major version or %NULL
792  * @minor: (out) (optional): Pointer to store minor version or %NULL
793  * @micro: (out) (optional): Pointer to store micro version or %NULL
794  *
795  * Fetch the runtime version of the libInstPatch library.
796  */
797 void
ipatch_version(guint * major,guint * minor,guint * micro)798 ipatch_version(guint *major, guint *minor, guint *micro)
799 {
800     if(major)
801     {
802         *major = IPATCH_VERSION_MAJOR;
803     }
804 
805     if(minor)
806     {
807         *minor = IPATCH_VERSION_MINOR;
808     }
809 
810     if(micro)
811     {
812         *micro = IPATCH_VERSION_MICRO;
813     }
814 }
815 
816 GQuark
ipatch_error_quark(void)817 ipatch_error_quark(void)
818 {
819     static GQuark q = 0;
820 
821     if(q == 0)
822     {
823         q = g_quark_from_static_string("libInstPatch-error-quark");
824     }
825 
826     return (q);
827 }
828 
829 /**
830  * _ret_g_log: (skip)
831  */
832 int
_ret_g_log(const gchar * log_domain,GLogLevelFlags log_level,const gchar * format,...)833 _ret_g_log(const gchar *log_domain, GLogLevelFlags log_level,
834            const gchar *format, ...)
835 {
836     va_list args;
837     va_start(args, format);
838     g_logv(log_domain, log_level, format, args);
839     va_end(args);
840 
841     return (TRUE);
842 }
843 
844 /**
845  * ipatch_gerror_message: (skip)
846  * @err:  A GError object or %NULL
847  *
848  * A utility function to check if a GError is set and return the
849  * GError's message field if it is, or a string explaining that there
850  * isn't any error info if @err is %NULL.
851  *
852  * Returns: The GError's message or a "&lt;No detailed error information&gt;" string.
853  */
854 G_CONST_RETURN char *
ipatch_gerror_message(GError * err)855 ipatch_gerror_message(GError *err)
856 {
857     return ((err) ? (err)->message : _("<No detailed error information>"));
858 }
859 
860 /**
861  * _ipatch_code_error: (skip)
862  *
863  * Internal function used by ipatch_code_error macros
864  */
865 void
_ipatch_code_error(const char * file,guint line,const char * func,GError ** err,const char * format,...)866 _ipatch_code_error(const char *file, guint line, const char *func,
867                    GError **err, const char *format, ...)
868 {
869     va_list args;
870     va_start(args, format);
871     _ipatch_code_errorv(file, line, func, err, format, args);
872     va_end(args);
873 }
874 
875 /**
876  * _ipatch_code_errorv: (skip)
877  *
878  * Internal function used by ipatch_code_error macros
879  */
880 void
_ipatch_code_errorv(const char * file,guint line,const char * func,GError ** err,const char * format,va_list args)881 _ipatch_code_errorv(const char *file, guint line, const char *func,
882                     GError **err, const char *format, va_list args)
883 {
884     char *msg, *loc, *temp;
885 
886     if(file && func)
887     {
888         loc = g_strdup_printf("%s:%d:%s()", file, line, func);
889     }
890     else if(file)
891     {
892         loc = g_strdup_printf("%s:%d", file, line);
893     }
894     else
895     {
896         loc = NULL;
897     }
898 
899     temp = g_strdup_vprintf(format, args);
900     msg = g_strdup_printf("%s - %s", loc, temp);
901     g_free(loc);
902     g_free(temp);
903 
904     g_critical("%s", msg);
905 
906     g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM,
907                 "Programmer error! (%s)", msg);
908     g_free(msg);
909 }
910 
911 /**
912  * ipatch_strconcat_num: (skip)
913  * @src: Source string
914  * @num: Number to concatenate
915  * @dest: Destination buffer
916  * @size: Size of destination buffer
917  *
918  * Creates a string with a number appended to it but ensures that it is
919  * of the specified @size (including NULL termination). Characters in the
920  * middle of the string are removed and a ".." is inserted, if necessary.
921  */
922 void
ipatch_strconcat_num(const char * src,int num,char * dest,int size)923 ipatch_strconcat_num(const char *src, int num, char *dest, int size)
924 {
925     char numstr[16];
926     int numlen, srclen, newlen, len1;
927     int remove;
928 
929     sprintf(numstr, "%d", num);
930     numlen = strlen(numstr);
931     srclen = strlen(src);
932 
933     remove = (srclen + numlen) - (size - 1);
934 
935     if(remove > 0)	       /* any characters need to be removed? */
936     {
937         remove += 2;		/* for ".." */
938         newlen = srclen - remove;	/* new length of non numeric string */
939         len1 = (newlen + 1) / 2;	/* length of first part before ".." */
940 
941         sprintf(dest, "%.*s..%.*s%s", len1, src,
942                 newlen - len1, src + (srclen - (newlen - len1)),
943                 numstr);
944     }
945     else
946     {
947         g_stpcpy(g_stpcpy(dest, src), numstr);
948     }
949 }
950 
951 /**
952  * ipatch_dump_object: (skip)
953  * @object: Object to dump
954  * @recursive: Set to %TRUE to recurse the @object children (if its a
955  *   #IpatchContainer derived object).
956  * @file: File to dump to or %NULL for stdout
957  *
958  * Dumps object info to a file for debugging purposes.
959  */
960 void
ipatch_dump_object(GObject * object,gboolean recursive,FILE * file)961 ipatch_dump_object(GObject *object, gboolean recursive, FILE *file)
962 {
963     char indent_buf[64] = "";
964 
965     g_return_if_fail(G_IS_OBJECT(object));
966 
967     if(!file)
968     {
969         file = stdout;
970     }
971 
972     if(!recursive)
973     {
974         dump_object_info(object, indent_buf, file);
975         fprintf(file, "</%s addr=%p>\n",
976                 g_type_name(G_TYPE_FROM_INSTANCE(object)), object);
977     }
978     else
979     {
980         dump_recursive(object, indent_buf, file);
981     }
982 }
983 
984 static void
dump_recursive(GObject * object,char * indent,FILE * file)985 dump_recursive(GObject *object, char *indent, FILE *file)
986 {
987     dump_object_info(object, indent, file);
988 
989     strcat(indent, "  ");	/* increase indent */
990 
991     if(IPATCH_IS_CONTAINER(object))
992     {
993         /* iterate over children if its an IpatchContainer */
994         IpatchList *list;
995         IpatchIter iter;
996         GObject *obj;
997 
998         list = ipatch_container_get_children(IPATCH_CONTAINER(object),
999                                              G_TYPE_OBJECT); /* ++ ref list */
1000         ipatch_list_init_iter(list, &iter);
1001 
1002         obj = ipatch_iter_first(&iter);
1003 
1004         if(obj)
1005         {
1006             fprintf(file, "\n");
1007         }
1008 
1009         while(obj)
1010         {
1011             dump_recursive(obj, indent, file);
1012             obj = ipatch_iter_next(&iter);
1013         }
1014 
1015         g_object_unref(list);	/* -- unref list */
1016     }
1017 
1018     indent[strlen(indent) - 2] = '\0';  /* decrease indent */
1019 
1020     fprintf(file, "%s</%s>\n", indent,
1021             g_type_name(G_TYPE_FROM_INSTANCE(object)));
1022 }
1023 
1024 static void
dump_object_info(GObject * object,char * indent,FILE * file)1025 dump_object_info(GObject *object, char *indent, FILE *file)
1026 {
1027     GParamSpec **pspecs, **pspec;
1028     GValue value = { 0 };
1029     char *contents;
1030 
1031     fprintf(file, "%s<%s addr=%p>\n", indent,
1032             g_type_name(G_TYPE_FROM_INSTANCE(object)), object);
1033 
1034     fprintf(file, "%s  refcount = %u\n", indent, object->ref_count);
1035 
1036     pspecs = g_object_class_list_properties(G_OBJECT_GET_CLASS(object), NULL);
1037     pspec = pspecs;
1038 
1039     while(*pspec)		/* write out property values */
1040     {
1041         if((*pspec)->flags & G_PARAM_READABLE)
1042         {
1043             g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(*pspec));
1044             g_object_get_property(object, g_param_spec_get_name(*pspec),
1045                                   &value);
1046             contents = g_strdup_value_contents(&value);
1047             g_value_unset(&value);
1048 
1049             fprintf(file, "%s  %s = %s\n", indent,
1050                     g_param_spec_get_name(*pspec), contents);
1051             g_free(contents);
1052         }
1053 
1054         pspec++;
1055     }
1056 
1057     g_free(pspecs);
1058 }
1059 
1060 /**
1061  * ipatch_glist_unref_free: (skip)
1062  * @objlist: List of GObjects
1063  *
1064  * Unreference each GObject in a GList and free the list.
1065  *
1066  * Since: 1.1.0
1067  */
1068 void
ipatch_glist_unref_free(GList * objlist)1069 ipatch_glist_unref_free(GList *objlist)
1070 {
1071     g_list_foreach(objlist, (GFunc) g_object_unref, NULL);
1072     g_list_free(objlist);
1073 }
1074