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: IpatchDLS2
22 * @short_description: DLS version 2 instrument file object
23 * @see_also:
24 * @stability: Stable
25 *
26 * Object type for DLS version 2 format instruments.
27 */
28 #include <stdio.h>
29 #include <string.h>
30 #include <glib.h>
31 #include <glib-object.h>
32 #include "IpatchDLS2.h"
33 #include "IpatchDLSFile.h"
34 #include "IpatchDLS2Info.h"
35 #include "IpatchDLS2Region.h"
36 #include "IpatchTypeProp.h"
37 #include "IpatchVirtualContainer_types.h"
38 #include "version.h"
39 #include "ipatch_priv.h"
40
41 /* non-INFO properties (INFO properties use their FOURCC int values) */
42 enum
43 {
44 PROP_0,
45 PROP_VERSION
46 };
47
48 static void ipatch_dls2_class_init(IpatchDLS2Class *klass);
49 static void ipatch_dls2_init(IpatchDLS2 *dls);
50 static void ipatch_dls2_finalize(GObject *gobject);
51 static void ipatch_dls2_set_property(GObject *object, guint property_id,
52 const GValue *value, GParamSpec *pspec);
53 static void ipatch_dls2_get_property(GObject *object, guint property_id,
54 GValue *value, GParamSpec *pspec);
55 static void ipatch_dls2_item_copy(IpatchItem *dest, IpatchItem *src,
56 IpatchItemCopyLinkFunc link_func,
57 gpointer user_data);
58
59 static const GType *ipatch_dls2_container_child_types(void);
60 static const GType *ipatch_dls2_container_virtual_types(void);
61 static gboolean ipatch_dls2_container_init_iter(IpatchContainer *container,
62 IpatchIter *iter, GType type);
63 static void ipatch_dls2_container_make_unique(IpatchContainer *container,
64 IpatchItem *item);
65 static void ipatch_dls2_base_find_unused_locale(IpatchBase *base, int *bank,
66 int *program,
67 const IpatchItem *exclude,
68 gboolean percussion);
69 static int locale_gcompare_func(gconstpointer a, gconstpointer b);
70 static IpatchItem *
71 ipatch_dls2_base_find_item_by_locale(IpatchBase *base, int bank, int program);
72
73 static gpointer parent_class = NULL;
74 static GType dls2_child_types[3] = { 0 };
75 static GType dls2_virt_types[4] = { 0 };
76
77
78
79 /* SoundFont item type creation function */
80 GType
ipatch_dls2_get_type(void)81 ipatch_dls2_get_type(void)
82 {
83 static GType item_type = 0;
84
85 if(!item_type)
86 {
87 static const GTypeInfo item_info =
88 {
89 sizeof(IpatchDLS2Class), NULL, NULL,
90 (GClassInitFunc)ipatch_dls2_class_init, NULL, NULL,
91 sizeof(IpatchDLS2),
92 0,
93 (GInstanceInitFunc)ipatch_dls2_init,
94 };
95
96 item_type = g_type_register_static(IPATCH_TYPE_BASE, "IpatchDLS2",
97 &item_info, 0);
98 }
99
100 return (item_type);
101 }
102
103 static void
ipatch_dls2_class_init(IpatchDLS2Class * klass)104 ipatch_dls2_class_init(IpatchDLS2Class *klass)
105 {
106 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
107 IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
108 IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass);
109 IpatchBaseClass *base_class = IPATCH_BASE_CLASS(klass);
110
111 parent_class = g_type_class_peek_parent(klass);
112
113 obj_class->finalize = ipatch_dls2_finalize;
114 obj_class->get_property = ipatch_dls2_get_property;
115
116 /* we use the IpatchItem item_set_property method */
117 item_class->item_set_property = ipatch_dls2_set_property;
118 item_class->copy = ipatch_dls2_item_copy;
119
120 container_class->child_types = ipatch_dls2_container_child_types;
121 container_class->virtual_types = ipatch_dls2_container_virtual_types;
122 container_class->init_iter = ipatch_dls2_container_init_iter;
123 container_class->make_unique = ipatch_dls2_container_make_unique;
124
125 base_class->find_unused_locale = ipatch_dls2_base_find_unused_locale;
126 base_class->find_item_by_locale = ipatch_dls2_base_find_item_by_locale;
127
128 /* non INFO properties */
129 g_object_class_override_property(obj_class, IPATCH_DLS2_NAME, "title");
130
131 g_object_class_install_property(obj_class, PROP_VERSION,
132 g_param_spec_string("version", _("Version"),
133 _("File version \"n.n.n.n\""),
134 NULL,
135 G_PARAM_READWRITE));
136
137 ipatch_dls2_info_install_class_properties(obj_class);
138
139 dls2_child_types[0] = IPATCH_TYPE_DLS2_INST;
140 dls2_child_types[1] = IPATCH_TYPE_DLS2_SAMPLE;
141
142 dls2_virt_types[0] = IPATCH_TYPE_VIRTUAL_DLS2_MELODIC;
143 dls2_virt_types[1] = IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION;
144 dls2_virt_types[2] = IPATCH_TYPE_VIRTUAL_DLS2_SAMPLES;
145 }
146
147 static void
ipatch_dls2_init(IpatchDLS2 * dls)148 ipatch_dls2_init(IpatchDLS2 *dls)
149 {
150 g_object_set(dls,
151 "name", _(IPATCH_BASE_DEFAULT_NAME),
152 "software", "libInstPatch v" IPATCH_VERSION,
153 NULL);
154
155 ipatch_item_clear_flags(IPATCH_ITEM(dls), IPATCH_BASE_CHANGED);
156 }
157
158 /* function called when SoundFont is being destroyed */
159 static void
ipatch_dls2_finalize(GObject * gobject)160 ipatch_dls2_finalize(GObject *gobject)
161 {
162 IpatchDLS2 *dls = IPATCH_DLS2(gobject);
163
164 IPATCH_ITEM_WLOCK(dls);
165
166 ipatch_dls2_info_free(dls->info);
167 dls->info = NULL;
168
169 g_free(dls->dlid);
170 dls->dlid = NULL;
171
172 IPATCH_ITEM_WUNLOCK(dls);
173
174 if(G_OBJECT_CLASS(parent_class)->finalize)
175 {
176 G_OBJECT_CLASS(parent_class)->finalize(gobject);
177 }
178 }
179
180 static void
ipatch_dls2_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)181 ipatch_dls2_set_property(GObject *object, guint property_id,
182 const GValue *value, GParamSpec *pspec)
183 {
184 IpatchDLS2 *dls = IPATCH_DLS2(object);
185 gboolean retval;
186
187 if(property_id == PROP_VERSION)
188 {
189 const char *verstr;
190 guint16 msu, msl, lsu, lsl;
191
192 verstr = g_value_get_string(value);
193
194 if(verstr && sscanf(verstr, "%hu.%hu.%hu.%hu",
195 &msu, &msl, &lsu, &lsl) != 4)
196 {
197 g_warning("Version property parse error");
198 return;
199 }
200
201 IPATCH_ITEM_WLOCK(dls);
202
203 if(verstr)
204 {
205 ipatch_item_set_flags(dls, IPATCH_DLS2_VERSION_SET);
206 dls->ms_version = (guint32)msu << 16 || msl;
207 dls->ls_version = (guint32)lsu << 16 || lsl;
208 }
209 else
210 {
211 ipatch_item_clear_flags(dls, IPATCH_DLS2_VERSION_SET);
212 }
213
214 IPATCH_ITEM_WUNLOCK(dls);
215 }
216 else
217 {
218 IPATCH_ITEM_WLOCK(dls);
219 retval = ipatch_dls2_info_set_property(&dls->info, property_id, value);
220 IPATCH_ITEM_WUNLOCK(dls);
221
222 /* check of "title" property needs to be notified, values do not need
223 to be sent, since its a read only property */
224 if(property_id == IPATCH_DLS2_NAME)
225 ipatch_item_prop_notify((IpatchItem *)dls, ipatch_item_pspec_title,
226 value, NULL);
227
228 if(!retval)
229 {
230 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
231 return;
232 }
233 }
234 }
235
236 static void
ipatch_dls2_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)237 ipatch_dls2_get_property(GObject *object, guint property_id,
238 GValue *value, GParamSpec *pspec)
239 {
240 IpatchDLS2 *dls;
241 gboolean retval;
242 char *s;
243
244 g_return_if_fail(IPATCH_IS_DLS2(object));
245 dls = IPATCH_DLS2(object);
246
247 if(property_id == PROP_VERSION)
248 {
249 gboolean version_set;
250 guint32 ms, ls;
251
252 IPATCH_ITEM_RLOCK(dls);
253
254 version_set = (ipatch_item_get_flags(dls) & IPATCH_DLS2_VERSION_SET) > 0;
255 ms = dls->ms_version;
256 ls = dls->ls_version;
257
258 IPATCH_ITEM_RUNLOCK(dls);
259
260 if(version_set)
261 {
262 s = g_strdup_printf("%u.%u.%u.%u", ms >> 16, ms & 0xFFFF,
263 ls >> 16, ls & 0xFFFF);
264 g_value_take_string(value, s);
265 }
266 else
267 {
268 g_value_set_string(value, NULL);
269 }
270 }
271 else /* INFO property or invalid */
272 {
273 IPATCH_ITEM_RLOCK(dls);
274 retval = ipatch_dls2_info_get_property(dls->info, property_id, value);
275 IPATCH_ITEM_RUNLOCK(dls);
276
277 if(!retval)
278 {
279 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
280 }
281 }
282 }
283
284 static void
ipatch_dls2_item_copy(IpatchItem * dest,IpatchItem * src,IpatchItemCopyLinkFunc link_func,gpointer user_data)285 ipatch_dls2_item_copy(IpatchItem *dest, IpatchItem *src,
286 IpatchItemCopyLinkFunc link_func, gpointer user_data)
287 {
288 IpatchDLS2 *src_dls, *dest_dls;
289 IpatchItem *newitem;
290 GHashTable *repl_samples;
291 GSList *p;
292
293 src_dls = IPATCH_DLS2(src);
294 dest_dls = IPATCH_DLS2(dest);
295
296 /* create item replacement hash */
297 repl_samples = g_hash_table_new(NULL, NULL);
298
299 IPATCH_ITEM_RLOCK(src_dls);
300
301 dest_dls->ms_version = src_dls->ms_version;
302 dest_dls->ls_version = src_dls->ls_version;
303
304 if(ipatch_item_get_flags(src_dls) & IPATCH_DLS2_VERSION_SET)
305 {
306 ipatch_item_set_flags(dest_dls, IPATCH_DLS2_VERSION_SET);
307 }
308
309 if(IPATCH_BASE(src_dls)->file)
310 {
311 ipatch_base_set_file(IPATCH_BASE(dest_dls), IPATCH_BASE(src_dls)->file);
312 }
313
314 dest_dls->info = ipatch_dls2_info_duplicate(src_dls->info);
315
316 if(src_dls->dlid)
317 {
318 dest_dls->dlid = g_memdup(src_dls->dlid, IPATCH_DLS_DLID_SIZE);
319 }
320
321 p = src_dls->samples;
322
323 while(p) /* duplicate samples */
324 {
325 newitem = ipatch_item_duplicate((IpatchItem *)(p->data));
326 g_return_if_fail(newitem != NULL);
327
328 dest_dls->samples = g_slist_prepend(dest_dls->samples, newitem);
329 ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_dls));
330
331 /* add to sample pointer replacement hash */
332 g_hash_table_insert(repl_samples, p->data, newitem);
333
334 /* FIXME - phase linked groups? */
335
336 p = g_slist_next(p);
337 }
338
339 p = src_dls->insts;
340
341 while(p) /* duplicate instruments */
342 {
343 newitem = ipatch_item_duplicate_replace((IpatchItem *)(p->data),
344 repl_samples);
345 g_return_if_fail(newitem != NULL);
346
347 dest_dls->insts = g_slist_prepend(dest_dls->insts, newitem);
348 ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_dls));
349
350 p = g_slist_next(p);
351 }
352
353 IPATCH_ITEM_RUNLOCK(src_dls);
354
355 dest_dls->insts = g_slist_reverse(dest_dls->insts);
356 dest_dls->samples = g_slist_reverse(dest_dls->samples);
357
358 g_hash_table_destroy(repl_samples);
359 }
360
361 static const GType *
ipatch_dls2_container_child_types(void)362 ipatch_dls2_container_child_types(void)
363 {
364 return (dls2_child_types);
365 }
366
367 static const GType *
ipatch_dls2_container_virtual_types(void)368 ipatch_dls2_container_virtual_types(void)
369 {
370 return (dls2_virt_types);
371 }
372
373 /* container is locked by caller */
374 static gboolean
ipatch_dls2_container_init_iter(IpatchContainer * container,IpatchIter * iter,GType type)375 ipatch_dls2_container_init_iter(IpatchContainer *container,
376 IpatchIter *iter, GType type)
377 {
378 IpatchDLS2 *dls = IPATCH_DLS2(container);
379
380 if(g_type_is_a(type, IPATCH_TYPE_DLS2_INST))
381 {
382 ipatch_iter_GSList_init(iter, &dls->insts);
383 }
384 else if(g_type_is_a(type, IPATCH_TYPE_DLS2_SAMPLE))
385 {
386 ipatch_iter_GSList_init(iter, &dls->samples);
387 }
388 else
389 {
390 g_critical("Invalid child type '%s' for parent of type '%s'",
391 g_type_name(type), g_type_name(G_OBJECT_TYPE(container)));
392 return (FALSE);
393 }
394
395 return (TRUE);
396 }
397
398 static void
ipatch_dls2_container_make_unique(IpatchContainer * container,IpatchItem * item)399 ipatch_dls2_container_make_unique(IpatchContainer *container,
400 IpatchItem *item)
401 {
402 IpatchDLS2 *dls = IPATCH_DLS2(container);
403 gboolean perc;
404 char *name, *newname;
405
406 IPATCH_ITEM_WLOCK(dls);
407
408 if(IPATCH_IS_DLS2_INST(item))
409 {
410 int bank, newbank, program, newprogram;
411 ipatch_dls2_inst_get_midi_locale(IPATCH_DLS2_INST(item),
412 &bank, &program);
413 newbank = bank;
414 newprogram = program;
415
416 perc = (ipatch_item_get_flags(item) & IPATCH_DLS2_INST_PERCUSSION) != 0;
417
418 ipatch_base_find_unused_midi_locale(IPATCH_BASE(dls),
419 &newbank, &newprogram, item, perc);
420
421 if(bank != newbank || program != newprogram)
422 ipatch_dls2_inst_set_midi_locale(IPATCH_DLS2_INST(item),
423 newbank, newprogram);
424 }
425 else if(!IPATCH_IS_DLS2_SAMPLE(item))
426 {
427 g_critical("Invalid child type '%s' for IpatchDLS2 object",
428 g_type_name(G_TYPE_FROM_INSTANCE(item)));
429 IPATCH_ITEM_WUNLOCK(dls);
430 return;
431 }
432
433 g_object_get(item, "name", &name, NULL);
434 newname = ipatch_dls2_make_unique_name(dls, G_TYPE_FROM_INSTANCE(item),
435 name, NULL);
436
437 if(!name || strcmp(name, newname) != 0)
438 {
439 g_object_set(item, "name", newname, NULL);
440 }
441
442 IPATCH_ITEM_WUNLOCK(dls);
443
444 g_free(name);
445 g_free(newname);
446 }
447
448 /* base method to find an unused MIDI bank:program locale */
449 static void
ipatch_dls2_base_find_unused_locale(IpatchBase * base,int * bank,int * program,const IpatchItem * exclude,gboolean percussion)450 ipatch_dls2_base_find_unused_locale(IpatchBase *base, int *bank,
451 int *program, const IpatchItem *exclude,
452 gboolean percussion)
453 {
454 IpatchDLS2 *dls = IPATCH_DLS2(base);
455 GSList *locale_list = NULL;
456 IpatchDLS2Inst *inst;
457 GSList *p;
458 guint32 b, n; /* Stores current bank and preset number */
459 guint32 lbank, lprogram;
460
461 /* fill array with bank and preset numbers */
462 IPATCH_ITEM_RLOCK(dls);
463 p = dls->insts;
464
465 while(p)
466 {
467 inst = (IpatchDLS2Inst *)(p->data);
468
469 /* only add to locale list if not the exclude item */
470 if((gpointer)inst != (gpointer)exclude)
471 locale_list = g_slist_prepend(locale_list, GUINT_TO_POINTER
472 ((inst->bank << 16) | inst->program));
473
474 p = g_slist_next(p);
475 }
476
477 IPATCH_ITEM_RUNLOCK(dls);
478
479 if(!locale_list)
480 {
481 *program = 0;
482 return;
483 }
484
485 locale_list = g_slist_sort(locale_list, (GCompareFunc)locale_gcompare_func);
486
487 b = (guint32) * bank;
488 n = (guint32) * program;
489
490 /* loop through sorted list of bank:programs */
491 p = locale_list;
492
493 while(p)
494 {
495 lprogram = GPOINTER_TO_UINT(p->data);
496 lbank = lprogram >> 16;
497 lprogram &= 0xFFFF;
498
499 if(lbank > b || (lbank == b && lprogram > n))
500 {
501 break;
502 }
503
504 if(lbank >= b)
505 {
506 if(++n > 127)
507 {
508 n = 0;
509 b++;
510 }
511 }
512
513 p = g_slist_delete_link(p, p); /* delete and advance */
514 }
515
516 *bank = b;
517 *program = n;
518
519 if(p)
520 {
521 g_slist_free(p); /* free remainder of list */
522 }
523 }
524
525 /* function used to do a temporary sort on preset list for
526 ipatch_dls2_base_find_unused_locale */
527 static int
locale_gcompare_func(gconstpointer a,gconstpointer b)528 locale_gcompare_func(gconstpointer a, gconstpointer b)
529 {
530 return (GPOINTER_TO_UINT(a) - GPOINTER_TO_UINT(b));
531 }
532
533 static IpatchItem *
ipatch_dls2_base_find_item_by_locale(IpatchBase * base,int bank,int program)534 ipatch_dls2_base_find_item_by_locale(IpatchBase *base, int bank, int program)
535 {
536 IpatchDLS2Inst *inst;
537
538 inst = ipatch_dls2_find_inst(IPATCH_DLS2(base), NULL, bank, program, NULL);
539 return ((IpatchItem *)inst);
540 }
541
542 /**
543 * ipatch_dls2_new:
544 *
545 * Create a new DLS base object.
546 *
547 * Returns: New DLS base object with a reference count of 1. Caller
548 * owns the reference and removing it will destroy the item.
549 */
550 IpatchDLS2 *
ipatch_dls2_new(void)551 ipatch_dls2_new(void)
552 {
553 return (IPATCH_DLS2(g_object_new(IPATCH_TYPE_DLS2, NULL)));
554 }
555
556 /**
557 * ipatch_dls2_set_file:
558 * @dls: DLS to set file object of
559 * @file: File object to use with the DLS object
560 *
561 * Sets the file object of a DLS object. DLS files are kept open
562 * for sample data that references the file. This function sets a
563 * DLS object's authoritive file. A convenience function, as
564 * ipatch_base_set_file() does the same thing (albeit without more specific
565 * type casting).
566 */
567 void
ipatch_dls2_set_file(IpatchDLS2 * dls,IpatchDLSFile * file)568 ipatch_dls2_set_file(IpatchDLS2 *dls, IpatchDLSFile *file)
569 {
570 g_return_if_fail(IPATCH_IS_DLS2(dls));
571 g_return_if_fail(IPATCH_IS_DLS_FILE(file));
572
573 ipatch_base_set_file(IPATCH_BASE(dls), IPATCH_FILE(file));
574 }
575
576 /**
577 * ipatch_dls2_get_file:
578 * @dls: DLS object to get file object of
579 *
580 * Gets the file object of a DLS. The returned DLS file object's
581 * reference count has been incremented. The caller owns the reference and is
582 * responsible for removing it with g_object_unref.
583 * A convenience function as ipatch_base_get_file() does the same thing
584 * (albeit without more specific type casting).
585 *
586 * Returns: (transfer full): The DLS file object or %NULL if @dls is not open. Remember
587 * to unref the file object with g_object_unref() when done with it.
588 */
589 IpatchDLSFile *
ipatch_dls2_get_file(IpatchDLS2 * dls)590 ipatch_dls2_get_file(IpatchDLS2 *dls)
591 {
592 IpatchFile *file;
593
594 g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL);
595
596 file = ipatch_base_get_file(IPATCH_BASE(dls));
597
598 if(file)
599 {
600 return (IPATCH_DLS_FILE(file));
601 }
602 else
603 {
604 return (NULL);
605 }
606 }
607
608 /**
609 * ipatch_dls2_get_info:
610 * @dls: DLS to get info from
611 * @fourcc: FOURCC integer id of INFO to get
612 *
613 * Get a DLS info string by FOURCC integer ID (integer representation of
614 * a 4 character RIFF chunk ID, see #IpatchRiff).
615 *
616 * Returns: New allocated info string value or %NULL if no info with the
617 * given @fourcc ID. String should be freed when finished with it.
618 */
619 char *
ipatch_dls2_get_info(IpatchDLS2 * dls,guint32 fourcc)620 ipatch_dls2_get_info(IpatchDLS2 *dls, guint32 fourcc)
621 {
622 char *val;
623
624 g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL);
625
626 IPATCH_ITEM_RLOCK(dls);
627 val = ipatch_dls2_info_get(dls->info, fourcc);
628 IPATCH_ITEM_RUNLOCK(dls);
629
630 return (val);
631 }
632
633 /**
634 * ipatch_dls2_set_info:
635 * @dls: DLS to set info of
636 * @fourcc: FOURCC integer ID of INFO to set
637 * @val: (nullable): Value to set info to or %NULL to unset (clear) info.
638 *
639 * Sets an INFO value in a DLS object.
640 *
641 * Emits changed signal on DLS object.
642 */
643 void
ipatch_dls2_set_info(IpatchDLS2 * dls,guint32 fourcc,const char * val)644 ipatch_dls2_set_info(IpatchDLS2 *dls, guint32 fourcc, const char *val)
645 {
646 GValue newval = { 0 }, oldval = { 0 };
647
648 g_return_if_fail(IPATCH_IS_DLS2(dls));
649
650 g_value_init(&newval, G_TYPE_STRING);
651 g_value_set_static_string(&newval, val);
652
653 g_value_init(&oldval, G_TYPE_STRING);
654 g_value_take_string(&oldval, ipatch_dls2_get_info(dls, fourcc));
655
656 IPATCH_ITEM_WLOCK(dls);
657 ipatch_dls2_info_set(&dls->info, fourcc, val);
658 IPATCH_ITEM_WUNLOCK(dls);
659
660 ipatch_dls2_info_notify((IpatchItem *)dls, fourcc, &newval, &oldval);
661
662 /* need to do title notify? */
663 if(fourcc == IPATCH_DLS2_NAME)
664 ipatch_item_prop_notify((IpatchItem *)dls, ipatch_item_pspec_title,
665 &newval, &oldval);
666
667 g_value_unset(&oldval);
668 g_value_unset(&newval);
669 }
670
671 /**
672 * ipatch_dls2_make_unique_name:
673 * @dls: DLS object
674 * @child_type: A child type of @dls to search for a unique name in
675 * @name: (nullable): An initial name to use or NULL
676 * @exclude: (nullable): An item to exclude from search or NULL
677 *
678 * Generates a unique name for the given @child_type in @dls. The @name
679 * parameter is used as a base and is modified, by appending a number, to
680 * make it unique (if necessary). The @exclude parameter is used to exclude
681 * an existing @dls child item from the search.
682 *
683 * MT-Note: To ensure that an item is actually unique before being
684 * added to a DLS object, ipatch_container_add_unique() should be
685 * used.
686 *
687 * Returns: A new unique name which should be freed when finished with it.
688 */
689 char *
ipatch_dls2_make_unique_name(IpatchDLS2 * dls,GType child_type,const char * name,const IpatchItem * exclude)690 ipatch_dls2_make_unique_name(IpatchDLS2 *dls, GType child_type,
691 const char *name, const IpatchItem *exclude)
692 {
693 GSList **list, *p;
694 char *curname, *numptr;
695 const char *temp;
696 guint count = 2, info_ofs, len;
697
698 g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL);
699
700 if(g_type_is_a(child_type, IPATCH_TYPE_DLS2_INST))
701 {
702 list = &dls->insts;
703 info_ofs = G_STRUCT_OFFSET(IpatchDLS2Inst, info);
704
705 if(!name || !*name)
706 {
707 name = _("New Instrument");
708 }
709 }
710 else if(g_type_is_a(child_type, IPATCH_TYPE_DLS2_SAMPLE))
711 {
712 list = &dls->samples;
713 info_ofs = G_STRUCT_OFFSET(IpatchDLS2Sample, info);
714
715 if(!name || !*name)
716 {
717 name = _("New Sample");
718 }
719 }
720 else
721 {
722 g_critical("Invalid child type '%s' of parent type '%s'",
723 g_type_name(child_type), g_type_name(G_OBJECT_TYPE(dls)));
724 return (NULL);
725 }
726
727 len = strlen(name);
728
729 /* allocate string size + 10 chars for number + zero termination */
730 curname = g_malloc0(len + 10 + 1);
731 strcpy(curname, name); /* copy name */
732 numptr = curname + len; /* pointer to end of name to concat number */
733
734 IPATCH_ITEM_RLOCK(dls);
735
736 p = *list;
737
738 while(p) /* check for duplicate */
739 {
740 IPATCH_ITEM_RLOCK(p->data); /* MT - Recursive LOCK */
741
742 if(p->data != exclude)
743 {
744 temp = ipatch_dls2_info_peek
745 (G_STRUCT_MEMBER(IpatchDLS2Info *, p->data, info_ofs),
746 IPATCH_DLS2_NAME);
747
748 if(temp && strcmp(temp, curname) == 0)
749 {
750 /* duplicate name */
751 IPATCH_ITEM_RUNLOCK(p->data);
752
753 sprintf(numptr, "%u", count++);
754
755 p = *list; /* start over */
756 continue;
757 }
758 }
759
760 IPATCH_ITEM_RUNLOCK(p->data);
761 p = g_slist_next(p);
762 }
763
764 IPATCH_ITEM_RUNLOCK(dls);
765
766 curname = g_realloc(curname, strlen(curname) + 1);
767 return (curname);
768 }
769
770 /**
771 * ipatch_dls2_find_inst:
772 * @dls: DLS object to search in
773 * @name: (nullable): Name of instrument to find or %NULL to match any name
774 * @bank: MIDI bank number of instrument to search for or -1 to not search by
775 * MIDI bank:program numbers
776 * @program: MIDI program number of instrument to search for, only used
777 * if @bank is 0-128
778 * @exclude: (nullable): A instrument to exclude from the search or %NULL
779 *
780 * Find a instrument by name or bank:program MIDI numbers. If instrument @name
781 * and @bank:@program are specified then match for either condition.
782 * If a instrument is found its reference count is incremented before it
783 * is returned. The caller is responsible for removing the reference
784 * with g_object_unref() when finished with it.
785 *
786 * Returns: (transfer full): The matching instrument or %NULL if not found. Remember to unref
787 * the item when finished with it.
788 */
789 IpatchDLS2Inst *
ipatch_dls2_find_inst(IpatchDLS2 * dls,const char * name,int bank,int program,const IpatchDLS2Inst * exclude)790 ipatch_dls2_find_inst(IpatchDLS2 *dls, const char *name, int bank,
791 int program, const IpatchDLS2Inst *exclude)
792 {
793 IpatchDLS2Inst *inst;
794 gboolean bynum = FALSE;
795 const char *temp = NULL;
796 GSList *p;
797
798 g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL);
799
800 /* if bank and program are valid, then search by number */
801 if(bank >= 0 && program >= 0 && program < 128)
802 {
803 bynum = TRUE;
804 }
805
806 IPATCH_ITEM_RLOCK(dls);
807 p = dls->insts;
808
809 while(p)
810 {
811 inst = (IpatchDLS2Inst *)(p->data);
812 IPATCH_ITEM_RLOCK(inst); /* MT - Recursive LOCK */
813
814 if(inst != exclude
815 && ((bynum && inst->bank == bank && inst->program == program)
816 || (name && (temp = ipatch_dls2_info_peek
817 (inst->info, IPATCH_DLS2_NAME))
818 && strcmp(temp, name) == 0)))
819 {
820 g_object_ref(inst);
821 IPATCH_ITEM_RUNLOCK(inst);
822 IPATCH_ITEM_RUNLOCK(dls);
823 return (inst);
824 }
825
826 IPATCH_ITEM_RUNLOCK(inst);
827 p = g_slist_next(p);
828 }
829
830 IPATCH_ITEM_RUNLOCK(dls);
831
832 return (NULL);
833 }
834
835 /**
836 * ipatch_dls2_find_sample:
837 * @dls: DLS object to search in
838 * @name: Name of sample to find
839 * @exclude: (nullable): A sample to exclude from the search or %NULL
840 *
841 * Find a sample by @name in a SoundFont. If a sample is found its
842 * reference count is incremented before it is returned. The caller
843 * is responsible for removing the reference with g_object_unref()
844 * when finished with it.
845 *
846 * Returns: (transfer full): The matching sample or %NULL if not found. Remember to unref
847 * the item when finished with it.
848 */
849 IpatchDLS2Sample *
ipatch_dls2_find_sample(IpatchDLS2 * dls,const char * name,const IpatchDLS2Sample * exclude)850 ipatch_dls2_find_sample(IpatchDLS2 *dls, const char *name,
851 const IpatchDLS2Sample *exclude)
852 {
853 IpatchDLS2Sample *sample;
854 const char *temp = NULL;
855 GSList *p;
856
857 g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL);
858 g_return_val_if_fail(name != NULL, NULL);
859
860 IPATCH_ITEM_RLOCK(dls);
861 p = dls->samples;
862
863 while(p)
864 {
865 sample = (IpatchDLS2Sample *)(p->data);
866 IPATCH_ITEM_RLOCK(sample); /* MT - Recursive LOCK */
867
868 if(sample != exclude && (temp = ipatch_dls2_info_peek
869 (sample->info, IPATCH_DLS2_NAME))
870 && strcmp(temp, name) == 0)
871 {
872 g_object_ref(sample);
873 IPATCH_ITEM_RUNLOCK(sample);
874 IPATCH_ITEM_RUNLOCK(dls);
875 return (sample);
876 }
877
878 IPATCH_ITEM_RUNLOCK(sample);
879 p = g_slist_next(p);
880 }
881
882 IPATCH_ITEM_RUNLOCK(dls);
883
884 return (NULL);
885 }
886
887 /**
888 * ipatch_dls2_get_region_references:
889 * @sample: Sample to locate referencing regions of.
890 *
891 * Get list of regions referencing an #IpatchDLS2Sample.
892 *
893 * Returns: (transfer full): New item list containing #IpatchDLS2Region objects
894 * that refer to @sample. The returned list has a reference count of 1 which
895 * the caller owns, unreference it when done.
896 */
897 IpatchList *
ipatch_dls2_get_region_references(IpatchDLS2Sample * sample)898 ipatch_dls2_get_region_references(IpatchDLS2Sample *sample)
899 {
900 IpatchDLS2 *dls;
901 IpatchDLS2Region *region;
902 IpatchList *refitems;
903 IpatchIter iter, region_iter;
904 IpatchDLS2Inst *inst;
905 IpatchItem *pitem;
906 gboolean success;
907
908 g_return_val_if_fail(IPATCH_IS_DLS2_SAMPLE(sample), NULL);
909
910 pitem = ipatch_item_get_parent(IPATCH_ITEM(sample));
911 g_return_val_if_fail(IPATCH_IS_DLS2(pitem), NULL);
912 dls = IPATCH_DLS2(pitem);
913
914 refitems = ipatch_list_new();
915
916 IPATCH_ITEM_RLOCK(dls);
917
918 success = ipatch_container_init_iter((IpatchContainer *)dls, &iter,
919 IPATCH_TYPE_DLS2_INST);
920 g_return_val_if_fail(success != FALSE, NULL);
921
922 inst = ipatch_dls2_inst_first(&iter);
923
924 while(inst) /* loop over instruments */
925 {
926 IPATCH_ITEM_RLOCK(inst); /* ## embedded lock */
927
928 success = ipatch_container_init_iter((IpatchContainer *)dls,
929 ®ion_iter,
930 IPATCH_TYPE_DLS2_INST);
931 g_return_val_if_fail(success != FALSE, NULL);
932
933 region = ipatch_dls2_region_first(®ion_iter);
934
935 while(region) /* loop over regions */
936 {
937 if(ipatch_dls2_region_peek_sample(region) == sample)
938 {
939 g_object_ref(region); /* ++ ref region for new iterator */
940 refitems->items = g_list_prepend(refitems->items, region);
941 }
942
943 region = ipatch_dls2_region_next(®ion_iter);
944 }
945
946 IPATCH_ITEM_RUNLOCK(inst);
947
948 inst = ipatch_dls2_inst_next(&iter);
949 }
950
951 IPATCH_ITEM_RUNLOCK(dls);
952
953 return (refitems);
954 }
955