1 /*
2 * libInstPatch
3 * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
4 *
5 * Author of this file: (C) 2012 BALATON Zoltan <balaton@eik.bme.hu>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; version 2.1
10 * of the License only.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA or on the web at http://www.gnu.org.
21 */
22 /**
23 * SECTION: IpatchSLI
24 * @short_description: Spectralis instrument file object
25 * @see_also:
26 *
27 * Object type for Spectralis format instruments.
28 */
29 #include <stdlib.h>
30 #include <string.h>
31 #include <glib.h>
32 #include <glib-object.h>
33 #include "IpatchSLI.h"
34 #include "IpatchSLIFile.h"
35 #include "IpatchSLIZone.h"
36 #include "IpatchParamProp.h"
37 #include "IpatchTypeProp.h"
38 #include "IpatchVirtualContainer_types.h"
39 #include "version.h"
40 #include "ipatch_priv.h"
41
42 /* properties */
43 enum
44 {
45 PROP_0,
46 PROP_TITLE
47 };
48
49 static void ipatch_sli_class_init(IpatchSLIClass *klass);
50 static void ipatch_sli_init(IpatchSLI *sli);
51 static void ipatch_sli_get_title(IpatchSLI *sli, GValue *value);
52 static void ipatch_sli_get_property(GObject *object, guint property_id,
53 GValue *value, GParamSpec *pspec);
54 static void ipatch_sli_item_copy(IpatchItem *dest, IpatchItem *src,
55 IpatchItemCopyLinkFunc link_func,
56 gpointer user_data);
57
58 static const GType *ipatch_sli_container_child_types(void);
59 static const GType *ipatch_sli_container_virtual_types(void);
60 static gboolean ipatch_sli_container_init_iter(IpatchContainer *container,
61 IpatchIter *iter, GType type);
62 static void ipatch_sli_container_make_unique(IpatchContainer *container,
63 IpatchItem *item);
64 static void ipatch_sli_parent_file_prop_notify(IpatchItemPropNotify *info);
65
66 static gpointer parent_class = NULL;
67 static GType sli_child_types[3] = { 0 };
68 static GType sli_virt_types[3] = { 0 };
69
70
71 /* Spectralis item type creation function */
72 GType
ipatch_sli_get_type(void)73 ipatch_sli_get_type(void)
74 {
75 static GType item_type = 0;
76
77 if(!item_type)
78 {
79 static const GTypeInfo item_info =
80 {
81 sizeof(IpatchSLIClass), NULL, NULL,
82 (GClassInitFunc)ipatch_sli_class_init, NULL, NULL,
83 sizeof(IpatchSLI),
84 0,
85 (GInstanceInitFunc)ipatch_sli_init,
86 };
87
88 item_type = g_type_register_static(IPATCH_TYPE_BASE, "IpatchSLI",
89 &item_info, 0);
90 }
91
92 return (item_type);
93 }
94
95 static void
ipatch_sli_class_init(IpatchSLIClass * klass)96 ipatch_sli_class_init(IpatchSLIClass *klass)
97 {
98 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
99 IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
100 IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass);
101
102 parent_class = g_type_class_peek_parent(klass);
103
104 obj_class->get_property = ipatch_sli_get_property;
105
106 item_class->copy = ipatch_sli_item_copy;
107
108 container_class->child_types = ipatch_sli_container_child_types;
109 container_class->virtual_types = ipatch_sli_container_virtual_types;
110 container_class->init_iter = ipatch_sli_container_init_iter;
111 container_class->make_unique = ipatch_sli_container_make_unique;
112
113 g_object_class_override_property(obj_class, PROP_TITLE, "title");
114
115 sli_child_types[0] = IPATCH_TYPE_SLI_INST;
116 sli_child_types[1] = IPATCH_TYPE_SLI_SAMPLE;
117
118 sli_virt_types[0] = IPATCH_TYPE_VIRTUAL_SLI_INST;
119 sli_virt_types[1] = IPATCH_TYPE_VIRTUAL_SLI_SAMPLES;
120 }
121
122 static void
ipatch_sli_init(IpatchSLI * sli)123 ipatch_sli_init(IpatchSLI *sli)
124 {
125 ipatch_item_clear_flags(IPATCH_ITEM(sli), IPATCH_BASE_CHANGED);
126 /* add a prop notify on file-name so sli can notify it's title also */
127 ipatch_item_prop_connect_by_name(IPATCH_ITEM(sli), "file-name",
128 ipatch_sli_parent_file_prop_notify, NULL,
129 sli);
130 }
131
132 static void
ipatch_sli_get_title(IpatchSLI * sli,GValue * value)133 ipatch_sli_get_title(IpatchSLI *sli, GValue *value)
134 {
135 char *filename;
136 gchar *s;
137
138 filename = ipatch_base_get_file_name(IPATCH_BASE(sli));
139 s = (filename ? g_path_get_basename(filename) : NULL);
140 free(filename);
141
142 if(!s || *s == G_DIR_SEPARATOR || *s == '.')
143 {
144 g_free(s);
145 s = g_strdup(_(IPATCH_BASE_DEFAULT_NAME));
146 }
147
148 g_value_take_string(value, s);
149 }
150
151 static void
ipatch_sli_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)152 ipatch_sli_get_property(GObject *object, guint property_id,
153 GValue *value, GParamSpec *pspec)
154 {
155 IpatchSLI *sli;
156
157 g_return_if_fail(IPATCH_IS_SLI(object));
158 sli = IPATCH_SLI(object);
159
160 switch(property_id)
161 {
162 case PROP_TITLE:
163 ipatch_sli_get_title(sli, value);
164 break;
165
166 default:
167 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
168 break;
169 }
170 }
171
172 static void
ipatch_sli_item_copy(IpatchItem * dest,IpatchItem * src,IpatchItemCopyLinkFunc link_func,gpointer user_data)173 ipatch_sli_item_copy(IpatchItem *dest, IpatchItem *src,
174 IpatchItemCopyLinkFunc link_func, gpointer user_data)
175 {
176 IpatchSLI *src_sli, *dest_sli;
177 IpatchItem *newitem;
178 GHashTable *repl_samples;
179 GSList *p;
180
181 src_sli = IPATCH_SLI(src);
182 dest_sli = IPATCH_SLI(dest);
183
184 /* create item replacement hash */
185 repl_samples = g_hash_table_new(NULL, NULL);
186
187 IPATCH_ITEM_RLOCK(src_sli);
188
189 if(IPATCH_BASE(src_sli)->file)
190 {
191 ipatch_base_set_file(IPATCH_BASE(dest_sli), IPATCH_BASE(src_sli)->file);
192 }
193
194 p = src_sli->samples;
195
196 while(p) /* duplicate samples */
197 {
198 /* ++ ref new duplicate sample, !! sample list takes it over */
199 newitem = ipatch_item_duplicate((IpatchItem *)(p->data));
200 dest_sli->samples = g_slist_prepend(dest_sli->samples, newitem);
201 ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sli));
202
203 /* add to sample pointer replacement hash */
204 g_hash_table_insert(repl_samples, p->data, newitem);
205
206 p = g_slist_next(p);
207 }
208
209 p = src_sli->insts;
210
211 while(p) /* duplicate instruments */
212 {
213 /* ++ ref new duplicate instrument, !! instrument list takes it over
214 * duplicate instrument and replace referenced sample pointers. */
215 newitem = ipatch_item_duplicate_replace((IpatchItem *)(p->data),
216 repl_samples);
217 dest_sli->insts = g_slist_prepend(dest_sli->insts, newitem);
218 ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sli));
219
220 p = g_slist_next(p);
221 }
222
223 IPATCH_ITEM_RUNLOCK(src_sli);
224
225 dest_sli->insts = g_slist_reverse(dest_sli->insts);
226 dest_sli->samples = g_slist_reverse(dest_sli->samples);
227
228 g_hash_table_destroy(repl_samples);
229 }
230
231 static const GType *
ipatch_sli_container_child_types(void)232 ipatch_sli_container_child_types(void)
233 {
234 return (sli_child_types);
235 }
236
237 static const GType *
ipatch_sli_container_virtual_types(void)238 ipatch_sli_container_virtual_types(void)
239 {
240 return (sli_virt_types);
241 }
242
243 /* container is locked by caller */
244 static gboolean
ipatch_sli_container_init_iter(IpatchContainer * container,IpatchIter * iter,GType type)245 ipatch_sli_container_init_iter(IpatchContainer *container,
246 IpatchIter *iter, GType type)
247 {
248 IpatchSLI *sli = IPATCH_SLI(container);
249
250 if(g_type_is_a(type, IPATCH_TYPE_SLI_INST))
251 {
252 ipatch_iter_GSList_init(iter, &sli->insts);
253 }
254 else if(g_type_is_a(type, IPATCH_TYPE_SLI_SAMPLE))
255 {
256 ipatch_iter_GSList_init(iter, &sli->samples);
257 }
258 else
259 {
260 g_critical("Invalid child type '%s' for parent of type '%s'",
261 g_type_name(type), g_type_name(G_OBJECT_TYPE(container)));
262 return (FALSE);
263 }
264
265 return (TRUE);
266 }
267
268 static void
ipatch_sli_container_make_unique(IpatchContainer * container,IpatchItem * item)269 ipatch_sli_container_make_unique(IpatchContainer *container,
270 IpatchItem *item)
271 {
272 IpatchSLI *sli = IPATCH_SLI(container);
273 char *name, *newname;
274
275 if(!(IPATCH_IS_SLI_INST(item) || IPATCH_IS_SLI_SAMPLE(item)))
276 {
277 g_critical("Invalid child type '%s' for IpatchSLI object",
278 g_type_name(G_TYPE_FROM_INSTANCE(item)));
279 return;
280 }
281
282 IPATCH_ITEM_WLOCK(sli);
283
284 g_object_get(item, "name", &name, NULL);
285 newname = ipatch_sli_make_unique_name(sli, G_TYPE_FROM_INSTANCE(item),
286 name, NULL);
287
288 if(!name || strcmp(name, newname) != 0)
289 {
290 g_object_set(item, "name", newname, NULL);
291 }
292
293 IPATCH_ITEM_WUNLOCK(sli);
294
295 g_free(name);
296 g_free(newname);
297 }
298
299 /* property notify for when parent's "file-name" property changes */
300 static void
ipatch_sli_parent_file_prop_notify(IpatchItemPropNotify * info)301 ipatch_sli_parent_file_prop_notify(IpatchItemPropNotify *info)
302 {
303 IpatchItem *sli = (IpatchItem *)(info->user_data);
304 /* notify that SLI's title has changed */
305 ipatch_item_prop_notify(sli, ipatch_item_pspec_title,
306 info->new_value, info->old_value);
307 }
308
309 /**
310 * ipatch_sli_new:
311 *
312 * Create a new Spectralis base object.
313 *
314 * Returns: New Spectralis base object with a reference count of 1. Caller
315 * owns the reference and removing it will destroy the item.
316 */
317 IpatchSLI *
ipatch_sli_new(void)318 ipatch_sli_new(void)
319 {
320 return (IPATCH_SLI(g_object_new(IPATCH_TYPE_SLI, NULL)));
321 }
322
323 /**
324 * ipatch_sli_set_file:
325 * @sli: SLI object to set file object of
326 * @file: File object to use with the SLI object
327 *
328 * Sets the file object of a SLI object. SLI files are kept open
329 * for sample data that references the file. This function sets a
330 * Spectralis object's authoritive file. A convenience function, as
331 * ipatch_base_set_file() does the same thing (albeit without more specific
332 * type casting).
333 */
334 void
ipatch_sli_set_file(IpatchSLI * sli,IpatchSLIFile * file)335 ipatch_sli_set_file(IpatchSLI *sli, IpatchSLIFile *file)
336 {
337 g_return_if_fail(IPATCH_IS_SLI(sli));
338 g_return_if_fail(IPATCH_IS_SLI_FILE(file));
339
340 ipatch_base_set_file(IPATCH_BASE(sli), IPATCH_FILE(file));
341 }
342
343 /**
344 * ipatch_sli_get_file:
345 * @sli: SLI object to get file object of
346 *
347 * Gets the file object of a SLI object. The returned SLI file object's
348 * reference count has been incremented. The caller owns the reference and is
349 * responsible for removing it with g_object_unref.
350 * A convenience function as ipatch_base_get_file() does the same thing
351 * (albeit without more specific type casting).
352 *
353 * Returns: (transfer full): The SLI file object or %NULL if @sli is not open. Remember
354 * to unref the file object with g_object_unref() when done with it.
355 */
356 IpatchSLIFile *
ipatch_sli_get_file(IpatchSLI * sli)357 ipatch_sli_get_file(IpatchSLI *sli)
358 {
359 IpatchFile *file;
360
361 g_return_val_if_fail(IPATCH_IS_SLI(sli), NULL);
362
363 file = ipatch_base_get_file(IPATCH_BASE(sli));
364
365 if(file)
366 {
367 return (IPATCH_SLI_FILE(file));
368 }
369 else
370 {
371 return (NULL);
372 }
373 }
374
375 /**
376 * ipatch_sli_make_unique_name:
377 * @sli: SLI object
378 * @child_type: A child type of @sli to search for a unique name in
379 * @name: (nullable): An initial name to use or %NULL
380 * @exclude: (nullable): An item to exclude from search or %NULL
381 *
382 * Generates a unique name for the given @child_type in @sli. The @name
383 * parameter is used as a base and is modified, by appending a number, to
384 * make it unique (if necessary). The @exclude parameter is used to exclude
385 * an existing @sli child item from the search.
386 *
387 * MT-Note: To ensure that an item is actually unique before being
388 * added to a SLI object, ipatch_container_add_unique() should be
389 * used.
390 *
391 * Returns: A new unique name which should be freed when finished with it.
392 */
393 char *
ipatch_sli_make_unique_name(IpatchSLI * sli,GType child_type,const char * name,const IpatchItem * exclude)394 ipatch_sli_make_unique_name(IpatchSLI *sli, GType child_type,
395 const char *name, const IpatchItem *exclude)
396 {
397 GSList **list, *p;
398 char curname[IPATCH_SLI_NAME_SIZE + 1];
399 guint name_ofs, count = 2;
400
401 g_return_val_if_fail(IPATCH_IS_SLI(sli), NULL);
402
403 if(g_type_is_a(child_type, IPATCH_TYPE_SLI_INST))
404 {
405 list = &sli->insts;
406 name_ofs = G_STRUCT_OFFSET(IpatchSLIInst, name);
407
408 if(!name || !*name)
409 {
410 name = _("New Instrument");
411 }
412 }
413 else if(g_type_is_a(child_type, IPATCH_TYPE_SLI_SAMPLE))
414 {
415 list = &sli->samples;
416 name_ofs = G_STRUCT_OFFSET(IpatchSLISample, name);
417
418 if(!name || !*name)
419 {
420 name = _("New Sample");
421 }
422 }
423 else
424 {
425 g_critical("Invalid child type '%s' of parent type '%s'",
426 g_type_name(child_type), g_type_name(G_OBJECT_TYPE(sli)));
427 return (NULL);
428 }
429
430 g_strlcpy(curname, name, sizeof(curname));
431
432 IPATCH_ITEM_RLOCK(sli);
433
434 p = *list;
435
436 while(p) /* check for duplicate */
437 {
438 IPATCH_ITEM_RLOCK(p->data); /* MT - Recursive LOCK */
439
440 if(p->data != exclude
441 && strcmp(G_STRUCT_MEMBER(char *, p->data, name_ofs),
442 curname) == 0)
443 {
444 /* duplicate name */
445 IPATCH_ITEM_RUNLOCK(p->data);
446
447 ipatch_strconcat_num(name, count++, curname, sizeof(curname));
448
449 p = *list; /* start over */
450 continue;
451 }
452
453 IPATCH_ITEM_RUNLOCK(p->data);
454 p = g_slist_next(p);
455 }
456
457 IPATCH_ITEM_RUNLOCK(sli);
458
459 return (g_strdup(curname));
460 }
461
462 /**
463 * ipatch_sli_find_inst:
464 * @sli: SLI to search in
465 * @name: Name of Instrument to find
466 * @exclude: (nullable): An instrument to exclude from the search or %NULL
467 *
468 * Find an instrument by @name in an SLI object. If a matching instrument
469 * is found, its reference count is incremented before it is returned.
470 * The caller is responsible for removing the reference with g_object_unref()
471 * when finished with it.
472 *
473 * Returns: (transfer full): The matching instrument or %NULL if not found. Remember to unref
474 * the item when finished with it.
475 */
476 IpatchSLIInst *
ipatch_sli_find_inst(IpatchSLI * sli,const char * name,const IpatchSLIInst * exclude)477 ipatch_sli_find_inst(IpatchSLI *sli, const char *name,
478 const IpatchSLIInst *exclude)
479 {
480 IpatchSLIInst *inst;
481 GSList *p;
482
483 g_return_val_if_fail(IPATCH_IS_SLI(sli), NULL);
484 g_return_val_if_fail(name != NULL, NULL);
485
486 IPATCH_ITEM_RLOCK(sli);
487 p = sli->insts;
488
489 while(p)
490 {
491 inst = (IpatchSLIInst *)(p->data);
492 IPATCH_ITEM_RLOCK(inst); /* MT - Recursive LOCK */
493
494 if(inst != exclude && strcmp(inst->name, name) == 0)
495 {
496 g_object_ref(inst);
497 IPATCH_ITEM_RUNLOCK(inst);
498 IPATCH_ITEM_RUNLOCK(sli);
499 return (inst);
500 }
501
502 IPATCH_ITEM_RUNLOCK(inst);
503 p = g_slist_next(p);
504 }
505
506 IPATCH_ITEM_RUNLOCK(sli);
507
508 return (NULL);
509 }
510
511 /**
512 * ipatch_sli_find_sample:
513 * @sli: SLI to search in
514 * @name: Name of sample to find
515 * @exclude: (nullable): A sample to exclude from the search or %NULL
516 *
517 * Find a sample by @name in a SLI object. If a sample is found its
518 * reference count is incremented before it is returned. The caller
519 * is responsible for removing the reference with g_object_unref()
520 * when finished with it.
521 *
522 * Returns: (transfer full): The matching sample or %NULL if not found. Remember to unref
523 * the item when finished with it.
524 */
525 IpatchSLISample *
ipatch_sli_find_sample(IpatchSLI * sli,const char * name,const IpatchSLISample * exclude)526 ipatch_sli_find_sample(IpatchSLI *sli, const char *name,
527 const IpatchSLISample *exclude)
528 {
529 IpatchSLISample *sample;
530 GSList *p;
531
532 g_return_val_if_fail(IPATCH_IS_SLI(sli), NULL);
533 g_return_val_if_fail(name != NULL, NULL);
534
535 IPATCH_ITEM_RLOCK(sli);
536 p = sli->samples;
537
538 while(p)
539 {
540 sample = (IpatchSLISample *)(p->data);
541 IPATCH_ITEM_RLOCK(sample); /* MT - Recursive LOCK */
542
543 if(p->data != exclude && strcmp(sample->name, name) == 0)
544 {
545 g_object_ref(sample);
546 IPATCH_ITEM_RUNLOCK(sample);
547 IPATCH_ITEM_RUNLOCK(sli);
548 return (p->data);
549 }
550
551 IPATCH_ITEM_RUNLOCK(sample);
552 p = g_slist_next(p);
553 }
554
555 IPATCH_ITEM_RUNLOCK(sli);
556
557 return (NULL);
558 }
559
560 /**
561 * ipatch_sli_get_zone_references:
562 * @sample: Sample to locate referencing zones of.
563 *
564 * Get list of zones referencing an IpatchSLISample.
565 *
566 * Returns: (transfer full): New item list containing #IpatchSLIZone objects
567 * that refer to @sample. The returned list has a reference count of 1 which
568 * the caller owns, unreference to free the list.
569 */
570 IpatchList *
ipatch_sli_get_zone_references(IpatchSLISample * sample)571 ipatch_sli_get_zone_references(IpatchSLISample *sample)
572 {
573 IpatchList *reflist, *instlist, *zonelist;
574 IpatchSLI *sli;
575 IpatchSLIZone *zone;
576 IpatchIter iter, zone_iter;
577 IpatchItem *pitem;
578
579 g_return_val_if_fail(IPATCH_IS_SLI_SAMPLE(sample), NULL);
580 pitem = ipatch_item_get_parent(IPATCH_ITEM(sample));
581 g_return_val_if_fail(IPATCH_IS_SLI(pitem), NULL);
582 sli = IPATCH_SLI(pitem);
583
584 reflist = ipatch_list_new(); /* ++ ref new list */
585 instlist = ipatch_sli_get_insts(sli); /* ++ ref instlist */
586
587 ipatch_list_init_iter(instlist, &iter);
588 pitem = ipatch_item_first(&iter);
589
590 while(pitem) /* loop over instruments */
591 {
592 zonelist = ipatch_sli_inst_get_zones(pitem); /* ++ ref new zone list */
593 ipatch_list_init_iter(zonelist, &zone_iter);
594
595 zone = ipatch_sli_zone_first(&zone_iter);
596
597 while(zone)
598 {
599 if(ipatch_sli_zone_peek_sample(zone) == sample)
600 {
601 g_object_ref(zone); /* ++ ref zone for new list */
602 reflist->items = g_list_prepend(reflist->items, zone);
603 }
604
605 zone = ipatch_sli_zone_next(&zone_iter);
606 }
607
608 g_object_unref(zonelist); /* -- unref zone list */
609 pitem = ipatch_item_next(&iter);
610 }
611
612 g_object_unref(instlist); /* -- unref instlist */
613
614 /* reverse list to preserve order since we prepended */
615 reflist->items = g_list_reverse(reflist->items);
616
617 return (reflist); /* !! caller takes over reference */
618 }
619