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: IpatchDLS2Sample
22  * @short_description: DLS audio sample object
23  * @see_also: #IpatchDLS, #IpatchDLSRegion
24  * @stability: Stable
25  *
26  * Object which defines a DLS audio sample.  These objects are contained in
27  * #IpatchDLS objects and linked (referenced) from #IpatchDLSRegion objects.
28  */
29 #include <stdarg.h>
30 #include <string.h>
31 #include <glib.h>
32 #include <glib-object.h>
33 #include "IpatchDLS2Sample.h"
34 #include "IpatchDLS2.h"
35 #include "IpatchDLSFile.h"
36 #include "IpatchDLSFile_priv.h"
37 #include "IpatchSample.h"
38 #include "IpatchSampleStoreRam.h"
39 #include "IpatchTypeProp.h"
40 #include "ipatch_priv.h"
41 #include "builtin_enums.h"
42 
43 /* properties */
44 enum
45 {
46     PROP_0,
47     PROP_SAMPLE_SIZE,		/* read only convenience property */
48     PROP_SAMPLE_FORMAT,
49     PROP_SAMPLE_RATE,
50     PROP_SAMPLE_DATA
51 };
52 
53 
54 /* sample info property enums, used by regions as well, so we define these
55    in a non-conflicting range
56    !! Keep order synchronized with IPATCH_DLS2_SAMPLE_INFO_PROPERTY_COUNT */
57 enum
58 {
59     PROP_FLAGS      = IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID,
60     PROP_LOOP_TYPE,
61     PROP_ROOT_NOTE,
62     PROP_FINE_TUNE,
63     PROP_GAIN,
64     PROP_LOOP_START,
65     PROP_LOOP_END
66 };
67 
68 /* for caching sample info GParamSpec objects for an object class */
69 typedef struct
70 {
71     GObjectClass *klass;		/* object class owning these properties */
72     GParamSpec *pspecs[IPATCH_DLS2_SAMPLE_INFO_PROPERTY_COUNT];
73 } ClassPropBag;
74 
75 static void ipatch_dls2_sample_iface_init(IpatchSampleIface *sample_iface);
76 static gboolean
77 ipatch_dls2_sample_iface_open(IpatchSampleHandle *handle, GError **err);
78 
79 static void ipatch_dls2_sample_finalize(GObject *gobject);
80 static void ipatch_dls2_sample_set_property(GObject *object,
81         guint property_id,
82         const GValue *value,
83         GParamSpec *pspec);
84 static void ipatch_dls2_sample_get_property(GObject *object,
85         guint property_id,
86         GValue *value,
87         GParamSpec *pspec);
88 static void ipatch_dls2_sample_item_copy(IpatchItem *dest, IpatchItem *src,
89         IpatchItemCopyLinkFunc link_func,
90         gpointer user_data);
91 static void ipatch_dls2_sample_item_remove_full(IpatchItem *item, gboolean full);
92 static gboolean ipatch_dls2_sample_real_set_data(IpatchDLS2Sample *sample,
93         IpatchSampleData *sampledata);
94 
95 
96 /* list of ClassPropBag to speed up info property notifies */
97 static GSList *info_pspec_list = NULL;
98 
99 
G_DEFINE_TYPE_WITH_CODE(IpatchDLS2Sample,ipatch_dls2_sample,IPATCH_TYPE_ITEM,G_IMPLEMENT_INTERFACE (IPATCH_TYPE_SAMPLE,ipatch_dls2_sample_iface_init))100 G_DEFINE_TYPE_WITH_CODE(IpatchDLS2Sample, ipatch_dls2_sample,
101                         IPATCH_TYPE_ITEM,
102                         G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE,
103                                 ipatch_dls2_sample_iface_init))
104 
105 /* ----- Initialization/deinitialization of ClassPropBag list ---------------*/
106 void _ipatch_DLS2_sample_init(void)
107 {
108     info_pspec_list = NULL;
109 }
110 
_ipatch_DLS2_sample_deinit()111 void _ipatch_DLS2_sample_deinit()
112 {
113     g_slist_free_full(info_pspec_list, g_free);
114 }
115 
116 /* ----- IpatchDSL2SAmple object functions  ---------------------------------*/
117 
118 /* sample interface initialization */
119 static void
ipatch_dls2_sample_iface_init(IpatchSampleIface * sample_iface)120 ipatch_dls2_sample_iface_init(IpatchSampleIface *sample_iface)
121 {
122     sample_iface->open = ipatch_dls2_sample_iface_open;
123     sample_iface->loop_types = ipatch_sample_loop_types_standard_release;
124 }
125 
126 static gboolean
ipatch_dls2_sample_iface_open(IpatchSampleHandle * handle,GError ** err)127 ipatch_dls2_sample_iface_open(IpatchSampleHandle *handle, GError **err)
128 {
129     IpatchDLS2Sample *sample = IPATCH_DLS2_SAMPLE(handle->sample);
130     g_return_val_if_fail(sample->sample_data != NULL, FALSE);
131     return (ipatch_sample_handle_cascade_open
132             (handle, (IpatchSample *)(sample->sample_data), err));
133 }
134 
135 static void
ipatch_dls2_sample_class_init(IpatchDLS2SampleClass * klass)136 ipatch_dls2_sample_class_init(IpatchDLS2SampleClass *klass)
137 {
138     GObjectClass *obj_class = G_OBJECT_CLASS(klass);
139     IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
140 
141     obj_class->finalize = ipatch_dls2_sample_finalize;
142     obj_class->get_property = ipatch_dls2_sample_get_property;
143 
144     /* we use the IpatchItem item_set_property method */
145     item_class->item_set_property = ipatch_dls2_sample_set_property;
146     item_class->copy = ipatch_dls2_sample_item_copy;
147     item_class->remove_full = ipatch_dls2_sample_item_remove_full;
148 
149     g_object_class_override_property(obj_class, IPATCH_DLS2_NAME, "title");
150 
151     ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size");
152     ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format");
153     ipatch_sample_install_property(obj_class, PROP_SAMPLE_RATE, "sample-rate");
154     ipatch_sample_install_property(obj_class, PROP_SAMPLE_DATA, "sample-data");
155 
156     ipatch_dls2_sample_info_install_class_properties(obj_class);
157     ipatch_dls2_info_install_class_properties(obj_class);
158 }
159 
160 static void
ipatch_dls2_sample_init(IpatchDLS2Sample * sample)161 ipatch_dls2_sample_init(IpatchDLS2Sample *sample)
162 {
163     ipatch_dls2_sample_set_blank(sample);
164     sample->rate = IPATCH_SAMPLE_RATE_DEFAULT;
165 }
166 
167 static void
ipatch_dls2_sample_finalize(GObject * gobject)168 ipatch_dls2_sample_finalize(GObject *gobject)
169 {
170     IpatchDLS2Sample *sample = IPATCH_DLS2_SAMPLE(gobject);
171 
172     /* nothing should reference the sample after this, but we set
173        pointers to NULL to help catch invalid references. Locking of
174        sample is required since in reality all its children do
175        still hold references */
176 
177     IPATCH_ITEM_WLOCK(sample);
178 
179     if(sample->sample_data)
180     {
181         ipatch_sample_data_unused(sample->sample_data);   // -- dec use count
182         g_object_unref(sample->sample_data);              // -- dec reference count
183     }
184 
185     if(sample->sample_info)
186     {
187         ipatch_dls2_sample_info_free(sample->sample_info);
188     }
189 
190     ipatch_dls2_info_free(sample->info);
191 
192     g_free(sample->dlid);
193 
194     IPATCH_ITEM_WUNLOCK(sample);
195 
196     if(G_OBJECT_CLASS(ipatch_dls2_sample_parent_class)->finalize)
197     {
198         G_OBJECT_CLASS(ipatch_dls2_sample_parent_class)->finalize(gobject);
199     }
200 }
201 
202 static void
ipatch_dls2_sample_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)203 ipatch_dls2_sample_set_property(GObject *object, guint property_id,
204                                 const GValue *value, GParamSpec *pspec)
205 {
206     IpatchDLS2Sample *sample = IPATCH_DLS2_SAMPLE(object);
207     gboolean retval;
208 
209     switch(property_id)
210     {
211     case PROP_SAMPLE_RATE:
212         IPATCH_ITEM_WLOCK(sample);
213         sample->rate = g_value_get_int(value);
214         IPATCH_ITEM_WUNLOCK(sample);
215         break;
216 
217     case PROP_SAMPLE_DATA:
218         ipatch_dls2_sample_real_set_data(sample, (IpatchSampleData *)
219                                          (g_value_get_object(value)));
220         break;
221 
222     default:
223         IPATCH_ITEM_WLOCK(sample);
224 
225         retval = ipatch_dls2_sample_info_set_property(&sample->sample_info,
226                  property_id, value);
227 
228         if(!retval)
229             retval = ipatch_dls2_info_set_property(&sample->info,
230                                                    property_id, value);
231 
232         IPATCH_ITEM_WUNLOCK(sample);
233 
234         /* check if "title" property needs to be notified */
235         if(property_id == IPATCH_DLS2_NAME)
236             ipatch_item_prop_notify((IpatchItem *)sample, ipatch_item_pspec_title,
237                                     value, NULL);
238 
239         if(!retval)
240         {
241             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
242             return;
243         }
244 
245         break;
246     }
247 }
248 
249 static void
ipatch_dls2_sample_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)250 ipatch_dls2_sample_get_property(GObject *object, guint property_id,
251                                 GValue *value, GParamSpec *pspec)
252 {
253     IpatchDLS2Sample *sample = IPATCH_DLS2_SAMPLE(object);
254     gboolean retval;
255 
256     switch(property_id)
257     {
258     case PROP_SAMPLE_SIZE:
259         g_return_if_fail(sample->sample_data != NULL);
260         g_object_get_property((GObject *)(sample->sample_data), "sample-size", value);
261         break;
262 
263     case PROP_SAMPLE_FORMAT:
264         g_return_if_fail(sample->sample_data != NULL);
265         g_object_get_property((GObject *)(sample->sample_data), "sample-format", value);
266         break;
267 
268     case PROP_SAMPLE_RATE:
269         IPATCH_ITEM_RLOCK(sample);
270         g_value_set_int(value, sample->rate);
271         IPATCH_ITEM_RUNLOCK(sample);
272         break;
273 
274     case PROP_SAMPLE_DATA:
275         g_value_take_object(value, ipatch_dls2_sample_get_data(sample));
276         break;
277 
278     default:
279         IPATCH_ITEM_RLOCK(sample);
280         retval = ipatch_dls2_sample_info_get_property(sample->sample_info,
281                  property_id, value);
282 
283         if(!retval)
284             retval = ipatch_dls2_info_get_property(sample->info,
285                                                    property_id, value);
286 
287         IPATCH_ITEM_RUNLOCK(sample);
288 
289         if(!retval)
290         {
291             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
292         }
293 
294         break;
295     }
296 }
297 
298 static void
ipatch_dls2_sample_item_copy(IpatchItem * dest,IpatchItem * src,IpatchItemCopyLinkFunc link_func,gpointer user_data)299 ipatch_dls2_sample_item_copy(IpatchItem *dest, IpatchItem *src,
300                              IpatchItemCopyLinkFunc link_func,
301                              gpointer user_data)
302 {
303     IpatchDLS2Sample *src_sam, *dest_sam;
304 
305     src_sam = IPATCH_DLS2_SAMPLE(src);
306     dest_sam = IPATCH_DLS2_SAMPLE(dest);
307 
308     IPATCH_ITEM_RLOCK(src_sam);
309 
310     dest_sam->sample_info = src_sam->sample_info ?
311                             ipatch_dls2_sample_info_duplicate(src_sam->sample_info) : NULL;
312     dest_sam->info = ipatch_dls2_info_duplicate(src_sam->info);
313     ipatch_dls2_sample_set_data(dest_sam, src_sam->sample_data);
314 
315     if(src_sam->dlid)
316     {
317         dest_sam->dlid = g_memdup(src_sam->dlid, IPATCH_DLS_DLID_SIZE);
318     }
319 
320     IPATCH_ITEM_RUNLOCK(src_sam);
321 }
322 
323 static void
ipatch_dls2_sample_item_remove_full(IpatchItem * item,gboolean full)324 ipatch_dls2_sample_item_remove_full(IpatchItem *item, gboolean full)
325 {
326     IpatchList *list;
327     IpatchIter iter;
328     IpatchItem *region;
329 
330     /* ++ ref new list */
331     list = ipatch_dls2_get_region_references(IPATCH_DLS2_SAMPLE(item));
332     ipatch_list_init_iter(list, &iter);
333 
334     region = ipatch_item_first(&iter);
335 
336     while(region)
337     {
338         ipatch_item_remove(region);
339         item = ipatch_item_next(&iter);
340     }
341 
342     g_object_unref(list);	/* -- unref list */
343 
344     if(full)
345     {
346         ipatch_dls2_sample_set_data(IPATCH_DLS2_SAMPLE(item), NULL);
347     }
348 
349     if(IPATCH_ITEM_CLASS(ipatch_dls2_sample_parent_class)->remove_full)
350     {
351         IPATCH_ITEM_CLASS(ipatch_dls2_sample_parent_class)->remove_full(item, full);
352     }
353 }
354 
355 /**
356  * ipatch_dls2_sample_new:
357  *
358  * Create a new DLS sample object.
359  *
360  * Returns: New DLS sample with a reference count of 1. Caller
361  * owns the reference and removing it will destroy the item, unless another
362  * reference is added (if its parented for example).
363  */
364 IpatchDLS2Sample *
ipatch_dls2_sample_new(void)365 ipatch_dls2_sample_new(void)
366 {
367     return (IPATCH_DLS2_SAMPLE(g_object_new(IPATCH_TYPE_DLS2_SAMPLE, NULL)));
368 }
369 
370 /**
371  * ipatch_dls2_sample_first: (skip)
372  * @iter: Patch item iterator containing #IpatchDLS2Sample items
373  *
374  * Gets the first item in a sample iterator. A convenience wrapper for
375  * ipatch_iter_first().
376  *
377  * Returns: The first sample in @iter or %NULL if empty.
378  */
379 IpatchDLS2Sample *
ipatch_dls2_sample_first(IpatchIter * iter)380 ipatch_dls2_sample_first(IpatchIter *iter)
381 {
382     GObject *obj;
383     g_return_val_if_fail(iter != NULL, NULL);
384 
385     obj = ipatch_iter_first(iter);
386 
387     if(obj)
388     {
389         return (IPATCH_DLS2_SAMPLE(obj));
390     }
391     else
392     {
393         return (NULL);
394     }
395 }
396 
397 /**
398  * ipatch_dls2_sample_next: (skip)
399  * @iter: Patch item iterator containing #IpatchDLS2Sample items
400  *
401  * Gets the next item in a sample iterator. A convenience wrapper for
402  * ipatch_iter_next().
403  *
404  * Returns: The next sample in @iter or %NULL if at the end of the list.
405  */
406 IpatchDLS2Sample *
ipatch_dls2_sample_next(IpatchIter * iter)407 ipatch_dls2_sample_next(IpatchIter *iter)
408 {
409     GObject *obj;
410     g_return_val_if_fail(iter != NULL, NULL);
411 
412     obj = ipatch_iter_next(iter);
413 
414     if(obj)
415     {
416         return (IPATCH_DLS2_SAMPLE(obj));
417     }
418     else
419     {
420         return (NULL);
421     }
422 }
423 
424 /**
425  * ipatch_dls2_sample_set_data:
426  * @sample: Sample to set sample data of
427  * @sampledata: Sample data to set sample to. Should be NULL or a IpatchSampleData object
428  *
429  * Set a sample's sample data object.
430  */
431 void
ipatch_dls2_sample_set_data(IpatchDLS2Sample * sample,IpatchSampleData * sampledata)432 ipatch_dls2_sample_set_data(IpatchDLS2Sample *sample, IpatchSampleData *sampledata)
433 {
434     if(ipatch_dls2_sample_real_set_data(sample, sampledata))
435     {
436         g_object_notify(G_OBJECT(sample), "sample-data");
437     }
438 }
439 
440 /* the actual setting of sample data, user routine does a g_object_notify */
441 static gboolean
ipatch_dls2_sample_real_set_data(IpatchDLS2Sample * sample,IpatchSampleData * sampledata)442 ipatch_dls2_sample_real_set_data(IpatchDLS2Sample *sample,
443                                  IpatchSampleData *sampledata)
444 {
445     IpatchSampleData *old_sampledata;
446 
447     g_return_val_if_fail(IPATCH_IS_DLS2_SAMPLE(sample), FALSE);
448     if(sampledata != NULL)
449     {
450         g_return_val_if_fail (IPATCH_IS_SAMPLE_DATA (sampledata), FALSE);
451         g_object_ref (sampledata);	/* ++ ref for sample */
452         ipatch_sample_data_used (sampledata);   /* ++ inc use count */
453     }
454 
455     IPATCH_ITEM_WLOCK(sample);
456     old_sampledata = sample->sample_data;
457     sample->sample_data = sampledata;	/* !! takes over ref */
458     IPATCH_ITEM_WUNLOCK(sample);
459 
460     if(old_sampledata)
461     {
462         ipatch_sample_data_unused(old_sampledata);   // -- dec use count
463         g_object_unref(old_sampledata);              // -- dec reference count
464     }
465 
466     return (TRUE);
467 }
468 
469 /**
470  * ipatch_dls2_sample_get_data:
471  * @sample: Sample to get sample data from
472  *
473  * Get the #IpatchSampleData item of a sample. Sample data item is referenced
474  * before returning and caller is responsible for unreferencing it with
475  * g_object_unref() when finished with it.
476  *
477  * Returns: (transfer full): Sample data object of sample or %NULL if none. Remember to
478  * unreference with g_object_unref() when finished with it.
479  */
480 IpatchSampleData *
ipatch_dls2_sample_get_data(IpatchDLS2Sample * sample)481 ipatch_dls2_sample_get_data(IpatchDLS2Sample *sample)
482 {
483     IpatchSampleData *sampledata;
484 
485     g_return_val_if_fail(IPATCH_IS_DLS2_SAMPLE(sample), NULL);
486 
487     IPATCH_ITEM_RLOCK(sample);
488     sampledata = sample->sample_data;
489 
490     if(sampledata)
491     {
492         g_object_ref(sampledata);    /* ++ ref */
493     }
494 
495     IPATCH_ITEM_RUNLOCK(sample);
496 
497     return (sampledata);	/* !! caller takes over ref */
498 }
499 
500 /**
501  * ipatch_dls2_sample_peek_data: (skip)
502  * @sample: Sample to get sample data from
503  *
504  * Get the #IpatchSampleData item of a sample. Like
505  * ipatch_dls2_sample_get_data() but sample data object is not referenced.
506  * This function should only be used if a reference of the sample data object
507  * is ensured or only the pointer value is of importance.
508  *
509  * Returns: (transfer none): Sample data object of sample or %NULL if none.
510  * Remember that a reference is NOT added.
511  */
512 IpatchSampleData *
ipatch_dls2_sample_peek_data(IpatchDLS2Sample * sample)513 ipatch_dls2_sample_peek_data(IpatchDLS2Sample *sample)
514 {
515     IpatchSampleData *sampledata;
516 
517     g_return_val_if_fail(IPATCH_IS_DLS2_SAMPLE(sample), NULL);
518 
519     IPATCH_ITEM_RLOCK(sample);
520     sampledata = sample->sample_data;
521     IPATCH_ITEM_RUNLOCK(sample);
522 
523     return (sampledata);
524 }
525 
526 /**
527  * ipatch_dls2_sample_set_blank:
528  * @sample: Sample to set to blank sample data
529  *
530  * Set the sample data of a sample item to blank data.
531  */
532 void
ipatch_dls2_sample_set_blank(IpatchDLS2Sample * sample)533 ipatch_dls2_sample_set_blank(IpatchDLS2Sample *sample)
534 {
535     IpatchSampleData *sampledata;
536 
537     g_return_if_fail(IPATCH_IS_DLS2_SAMPLE(sample));
538 
539     sampledata = ipatch_sample_data_get_blank();
540 
541     IPATCH_ITEM_WLOCK(sample);
542 
543     if(sample->sample_info)	/* reset sample info to defaults */
544     {
545         ipatch_dls2_sample_info_free(sample->sample_info);
546         sample->sample_info = NULL;
547     }
548 
549     g_object_set(sample,
550                  "sample-data", sampledata,
551                  "sample-rate", 44100,
552                  NULL);
553     IPATCH_ITEM_WUNLOCK(sample);
554 
555     g_object_unref(sampledata);
556 }
557 
558 GType
ipatch_dls2_sample_info_get_type(void)559 ipatch_dls2_sample_info_get_type(void)
560 {
561     static GType type = 0;
562 
563     if(!type)
564         type = g_boxed_type_register_static("IpatchDLS2SampleInfo",
565                                             (GBoxedCopyFunc)ipatch_dls2_sample_info_duplicate,
566                                             (GBoxedFreeFunc)ipatch_dls2_sample_info_free);
567 
568     return (type);
569 }
570 
571 /**
572  * ipatch_dls2_sample_info_new:
573  *
574  * Allocates a new sample info structure.
575  *
576  * Returns: (transfer full): New sample info structure, free it with
577  * ipatch_dls2_sample_info_free() when finished.
578  */
579 IpatchDLS2SampleInfo *
ipatch_dls2_sample_info_new(void)580 ipatch_dls2_sample_info_new(void)
581 {
582     IpatchDLS2SampleInfo *sample_info;
583 
584     sample_info = g_slice_new0(IpatchDLS2SampleInfo);
585     sample_info->root_note = 60;
586 
587     return (sample_info);
588 }
589 
590 /**
591  * ipatch_dls2_sample_info_free:
592  * @sample_info: Sample info structure
593  *
594  * Free a sample info structure allocated with ipatch_dls2_sample_info_new().
595  */
596 void
ipatch_dls2_sample_info_free(IpatchDLS2SampleInfo * sample_info)597 ipatch_dls2_sample_info_free(IpatchDLS2SampleInfo *sample_info)
598 {
599     g_slice_free(IpatchDLS2SampleInfo, sample_info);
600 }
601 
602 /**
603  * ipatch_dls2_sample_info_duplicate:
604  * @sample_info: Sample info structure to duplicate
605  *
606  * Duplicate a sample info structure.
607  *
608  * Returns: Newly allocated sample info structure which should be freed
609  * with ipatch_dls2_sample_info_free() when done with it.
610  */
611 IpatchDLS2SampleInfo *
ipatch_dls2_sample_info_duplicate(IpatchDLS2SampleInfo * sample_info)612 ipatch_dls2_sample_info_duplicate(IpatchDLS2SampleInfo *sample_info)
613 {
614     IpatchDLS2SampleInfo *newinfo;
615 
616     g_return_val_if_fail(sample_info != NULL, NULL);
617 
618     newinfo = ipatch_dls2_sample_info_new();
619     *newinfo = *sample_info;
620 
621     return (newinfo);
622 }
623 
624 /**
625  * ipatch_dls2_sample_info_init:
626  * @sample_info: Sample info structure to initialize
627  *
628  * Initialize a sample info structure to defaults.
629  */
630 void
ipatch_dls2_sample_info_init(IpatchDLS2SampleInfo * sample_info)631 ipatch_dls2_sample_info_init(IpatchDLS2SampleInfo *sample_info)
632 {
633     g_return_if_fail(sample_info != NULL);
634 
635     memset(sample_info, 0, sizeof(IpatchDLS2SampleInfo));
636     sample_info->root_note = 60;
637 }
638 
639 /**
640  * ipatch_dls2_sample_info_install_class_properties: (skip)
641  * @obj_class: GObjectClass to install properties for
642  *
643  * Installs sample info properties for the given @obj_class. Useful for
644  * objects that implement #IpatchDLS2SampleInfo properties.
645  */
646 void
ipatch_dls2_sample_info_install_class_properties(GObjectClass * obj_class)647 ipatch_dls2_sample_info_install_class_properties(GObjectClass *obj_class)
648 {
649     ClassPropBag *bag;
650 
651     /* add new bag to cache pspecs for this class */
652     bag = g_new(ClassPropBag, 1);
653     bag->klass = obj_class;
654     info_pspec_list = g_slist_append(info_pspec_list, bag);
655 
656     /* properties defined by IpatchSample interface */
657     bag->pspecs[0]
658         = ipatch_sample_install_property(obj_class, PROP_LOOP_TYPE, "loop-type");
659     bag->pspecs[1]
660         = ipatch_sample_install_property(obj_class, PROP_LOOP_START, "loop-start");
661     bag->pspecs[2]
662         = ipatch_sample_install_property(obj_class, PROP_LOOP_END, "loop-end");
663     bag->pspecs[3]
664         = ipatch_sample_install_property(obj_class, PROP_ROOT_NOTE, "root-note");
665     bag->pspecs[4]
666         = ipatch_sample_install_property(obj_class, PROP_FINE_TUNE, "fine-tune");
667 
668     bag->pspecs[5]
669         = g_param_spec_flags("flags", _("Sample flags"),
670                              _("Sample flags"),
671                              IPATCH_TYPE_DLS2_SAMPLE_FLAGS,
672                              0,
673                              G_PARAM_READWRITE);
674     g_object_class_install_property(obj_class, PROP_FLAGS, bag->pspecs[5]);
675 
676     bag->pspecs[6]
677         = g_param_spec_int("gain", _("Gain"),
678                            _("Gain in DLS relative gain units"),
679                            G_MININT, G_MAXINT, 0,
680                            G_PARAM_READWRITE);
681     g_object_class_install_property(obj_class, PROP_GAIN, bag->pspecs[6]);
682 }
683 
684 /**
685  * ipatch_dls2_sample_info_is_property_id_valid: (skip)
686  * @property_id: Property ID to test
687  *
688  * Check if a property ID is a valid sample info property ID.
689  *
690  * Returns: %TRUE if property_id is a sample info property ID, %FALSE otherwise.
691  */
692 gboolean
ipatch_dls2_sample_info_is_property_id_valid(guint property_id)693 ipatch_dls2_sample_info_is_property_id_valid(guint property_id)
694 {
695     return (property_id == PROP_FLAGS || property_id == PROP_LOOP_TYPE
696             || property_id == PROP_ROOT_NOTE || property_id == PROP_FINE_TUNE
697             || property_id == PROP_GAIN || property_id == PROP_LOOP_START
698             || property_id == PROP_LOOP_END);
699 }
700 
701 /**
702  * ipatch_dls2_sample_info_set_property: (skip)
703  * @sample_info: Pointer to pointer to sample info
704  * @property_id: Property ID
705  * @value: Value for property
706  *
707  * A function used by set_property methods that implement #IpatchDLS2SampleInfo
708  * properties.
709  *
710  * Returns: %TRUE if property_id was handled, %FALSE otherwise
711  */
712 gboolean
ipatch_dls2_sample_info_set_property(IpatchDLS2SampleInfo ** sample_info,guint property_id,const GValue * value)713 ipatch_dls2_sample_info_set_property(IpatchDLS2SampleInfo **sample_info,
714                                      guint property_id, const GValue *value)
715 {
716     IpatchDLS2SampleInfo *saminfo;
717 
718     if(!*sample_info)
719     {
720         if(property_id != PROP_FLAGS && property_id != PROP_LOOP_TYPE
721                 && property_id != PROP_ROOT_NOTE && property_id != PROP_FINE_TUNE
722                 && property_id != PROP_GAIN && property_id != PROP_LOOP_START
723                 && property_id != PROP_LOOP_END)
724         {
725             return (FALSE);
726         }
727 
728         *sample_info = ipatch_dls2_sample_info_new();
729     }
730 
731     saminfo = *sample_info;
732 
733     switch(property_id)
734     {
735     case PROP_FLAGS:
736         saminfo->options &= ~IPATCH_DLS2_SAMPLE_FLAGS_MASK;
737         saminfo->options |= g_value_get_flags(value)
738                             & IPATCH_DLS2_SAMPLE_FLAGS_MASK;
739         break;
740 
741     case PROP_LOOP_TYPE:
742         saminfo->options &= ~IPATCH_DLS2_SAMPLE_LOOP_MASK;
743         saminfo->options |= g_value_get_enum(value)
744                             & IPATCH_DLS2_SAMPLE_LOOP_MASK;
745         break;
746 
747     case PROP_ROOT_NOTE:
748         saminfo->root_note = g_value_get_int(value);
749         break;
750 
751     case PROP_FINE_TUNE:
752         saminfo->fine_tune = g_value_get_int(value);
753         break;
754 
755     case PROP_GAIN:
756         saminfo->gain = g_value_get_int(value);
757         break;
758 
759     case PROP_LOOP_START:
760         saminfo->loop_start = g_value_get_uint(value);
761         break;
762 
763     case PROP_LOOP_END:
764         saminfo->loop_end = g_value_get_uint(value);
765         break;
766 
767     default:
768         return (FALSE);
769     }
770 
771     return (TRUE);
772 }
773 
774 /**
775  * ipatch_dls2_sample_info_get_property: (skip)
776  * @sample_info: Pointer to sample info
777  * @property_id: Property ID
778  * @value: Value to set
779  *
780  * A function used by get_property methods that implement #IpatchDLS2SampleInfo
781  * properties.
782  *
783  * Returns: %TRUE if property_id was handled, %FALSE otherwise
784  */
785 gboolean
ipatch_dls2_sample_info_get_property(IpatchDLS2SampleInfo * sample_info,guint property_id,GValue * value)786 ipatch_dls2_sample_info_get_property(IpatchDLS2SampleInfo *sample_info,
787                                      guint property_id, GValue *value)
788 {
789     switch(property_id)
790     {
791     case PROP_FLAGS:
792         g_value_set_flags(value, sample_info ?
793                           (sample_info->options
794                            & IPATCH_DLS2_SAMPLE_FLAGS_MASK) : 0);
795         break;
796 
797     case PROP_LOOP_TYPE:
798         g_value_set_enum(value, sample_info ?
799                          (sample_info->options
800                           & IPATCH_DLS2_SAMPLE_LOOP_MASK)
801                          : IPATCH_SAMPLE_LOOP_NONE);
802         break;
803 
804     case PROP_ROOT_NOTE:
805         g_value_set_int(value, sample_info ? sample_info->root_note : 60);
806         break;
807 
808     case PROP_FINE_TUNE:
809         g_value_set_int(value, sample_info ? sample_info->fine_tune : 0);
810         break;
811 
812     case PROP_GAIN:
813         g_value_set_int(value, sample_info ? sample_info->gain : 0);
814         break;
815 
816     case PROP_LOOP_START:
817         g_value_set_uint(value, sample_info ? sample_info->loop_start : 0);
818         break;
819 
820     case PROP_LOOP_END:
821         g_value_set_uint(value, sample_info ? sample_info->loop_end : 0);
822         break;
823 
824     default:
825         return (FALSE);
826     }
827 
828     return (TRUE);
829 }
830 
831 /**
832  * ipatch_dls2_sample_info_notify_changes: (skip)
833  * @item: Item to send #IpatchItem property notifies on
834  * @newinfo: New sample info values
835  * @oldinfo: Old sample info values
836  *
837  * Sends #IpatchItem property notifies for changed sample info parameters.
838  */
839 void
ipatch_dls2_sample_info_notify_changes(IpatchItem * item,IpatchDLS2SampleInfo * newinfo,IpatchDLS2SampleInfo * oldinfo)840 ipatch_dls2_sample_info_notify_changes(IpatchItem *item,
841                                        IpatchDLS2SampleInfo *newinfo,
842                                        IpatchDLS2SampleInfo *oldinfo)
843 {
844     GParamSpec **found_pspec_cache = NULL;
845     GObjectClass *klass;
846     GValue newval = { 0 }, oldval = { 0 };
847     GSList *p;
848 
849     g_return_if_fail(IPATCH_IS_ITEM(item));
850 
851     klass = G_OBJECT_GET_CLASS(item);
852 
853     /* search for param spec cache for object's class */
854     for(p = info_pspec_list; p; p = p->next)
855     {
856         if(((ClassPropBag *)(p->data))->klass == klass)
857         {
858             found_pspec_cache = ((ClassPropBag *)(p->data))->pspecs;
859             break;
860         }
861     }
862 
863     g_return_if_fail(found_pspec_cache);
864 
865     if((oldinfo->options & IPATCH_DLS2_SAMPLE_LOOP_MASK)
866             != (newinfo->options & IPATCH_DLS2_SAMPLE_LOOP_MASK))
867     {
868         g_value_init(&newval, IPATCH_TYPE_SAMPLE_LOOP_TYPE);
869         g_value_init(&oldval, IPATCH_TYPE_SAMPLE_LOOP_TYPE);
870         g_value_set_enum(&newval, newinfo->options & IPATCH_DLS2_SAMPLE_LOOP_MASK);
871         g_value_set_enum(&oldval, oldinfo->options & IPATCH_DLS2_SAMPLE_LOOP_MASK);
872         ipatch_item_prop_notify(item, found_pspec_cache[0], &newval, &oldval);
873         g_value_unset(&newval);
874         g_value_unset(&oldval);
875     }
876 
877     if((oldinfo->options & IPATCH_DLS2_SAMPLE_FLAGS_MASK)
878             != (newinfo->options & IPATCH_DLS2_SAMPLE_FLAGS_MASK))
879     {
880         g_value_init(&newval, IPATCH_TYPE_DLS2_SAMPLE_FLAGS);
881         g_value_init(&oldval, IPATCH_TYPE_DLS2_SAMPLE_FLAGS);
882         g_value_set_flags(&newval, newinfo->options & IPATCH_DLS2_SAMPLE_FLAGS_MASK);
883         g_value_set_flags(&oldval, oldinfo->options & IPATCH_DLS2_SAMPLE_FLAGS_MASK);
884         ipatch_item_prop_notify(item, found_pspec_cache[1], &newval, &oldval);
885         g_value_unset(&newval);
886         g_value_unset(&oldval);
887     }
888 
889     if(oldinfo->root_note != newinfo->root_note)
890     {
891         g_value_init(&newval, G_TYPE_INT);
892         g_value_init(&oldval, G_TYPE_INT);
893         g_value_set_int(&newval, newinfo->root_note);
894         g_value_set_int(&oldval, oldinfo->root_note);
895         ipatch_item_prop_notify(item, found_pspec_cache[2], &newval, &oldval);
896         g_value_unset(&newval);
897         g_value_unset(&oldval);
898     }
899 
900     if(oldinfo->fine_tune != newinfo->fine_tune)
901     {
902         g_value_init(&newval, G_TYPE_INT);
903         g_value_init(&oldval, G_TYPE_INT);
904         g_value_set_int(&newval, newinfo->fine_tune);
905         g_value_set_int(&oldval, oldinfo->fine_tune);
906         ipatch_item_prop_notify(item, found_pspec_cache[3], &newval, &oldval);
907         g_value_unset(&newval);
908         g_value_unset(&oldval);
909     }
910 
911     if(oldinfo->gain != newinfo->gain)
912     {
913         g_value_init(&newval, G_TYPE_INT);
914         g_value_init(&oldval, G_TYPE_INT);
915         g_value_set_int(&newval, newinfo->gain);
916         g_value_set_int(&oldval, oldinfo->gain);
917         ipatch_item_prop_notify(item, found_pspec_cache[4], &newval, &oldval);
918         g_value_unset(&newval);
919         g_value_unset(&oldval);
920     }
921 
922     if(oldinfo->loop_start != newinfo->loop_start)
923     {
924         g_value_init(&newval, G_TYPE_UINT);
925         g_value_init(&oldval, G_TYPE_UINT);
926         g_value_set_uint(&newval, newinfo->loop_start);
927         g_value_set_uint(&oldval, oldinfo->loop_start);
928         ipatch_item_prop_notify(item, found_pspec_cache[5], &newval, &oldval);
929         g_value_unset(&newval);
930         g_value_unset(&oldval);
931     }
932 
933     if(oldinfo->loop_end != newinfo->loop_end)
934     {
935         g_value_init(&newval, G_TYPE_UINT);
936         g_value_init(&oldval, G_TYPE_UINT);
937         g_value_set_uint(&newval, newinfo->loop_end);
938         g_value_set_uint(&oldval, oldinfo->loop_end);
939         ipatch_item_prop_notify(item, found_pspec_cache[6], &newval, &oldval);
940         g_value_unset(&newval);
941         g_value_unset(&oldval);
942     }
943 }
944