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 "<No detailed error information>" 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