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: IpatchSF2Sample
22  * @short_description: SoundFont audio sample
23  * @see_also: #IpatchSF2, #IpatchSF2IZone
24  * @stability: Stable
25  *
26  * SoundFont samples are children of #IpatchSF2 objects and are referenced
27  * by #IpatchSF2IZone objects.  They define the audio which is synthesized.
28  */
29 #include <stdarg.h>
30 #include <glib.h>
31 #include <glib-object.h>
32 #include "IpatchSF2Sample.h"
33 #include "IpatchSample.h"
34 #include "IpatchSampleData.h"
35 #include "IpatchSF2.h"
36 #include "IpatchSF2File.h"
37 #include "IpatchParamProp.h"
38 #include "IpatchTypeProp.h"
39 #include "IpatchSF2VoiceCache.h"
40 #include "builtin_enums.h"
41 #include "ipatch_priv.h"
42 
43 /* properties */
44 enum
45 {
46     PROP_0,
47     PROP_NAME,
48     PROP_SAMPLE_SIZE,
49     PROP_SAMPLE_FORMAT,
50     PROP_SAMPLE_RATE,
51     PROP_LOOP_TYPE,
52     PROP_LOOP_START,
53     PROP_LOOP_END,
54     PROP_ROOT_NOTE,
55     PROP_FINE_TUNE,
56     PROP_CHANNEL,
57     PROP_ROM,
58     PROP_SAMPLE_DATA,
59     PROP_LINKED_SAMPLE
60 };
61 
62 static void ipatch_sf2_sample_iface_init(IpatchSampleIface *sample_iface);
63 static gboolean ipatch_sf2_sample_iface_open(IpatchSampleHandle *handle,
64         GError **err);
65 
66 static void ipatch_sf2_sample_finalize(GObject *gobject);
67 static void ipatch_sf2_sample_set_property(GObject *object,
68         guint property_id,
69         const GValue *value,
70         GParamSpec *pspec);
71 static void ipatch_sf2_sample_get_property(GObject *object,
72         guint property_id,
73         GValue *value,
74         GParamSpec *pspec);
75 static void ipatch_sf2_sample_item_copy(IpatchItem *dest, IpatchItem *src,
76                                         IpatchItemCopyLinkFunc link_func,
77                                         gpointer user_data);
78 static void ipatch_sf2_sample_item_remove_full(IpatchItem *item, gboolean full);
79 static int
80 ipatch_sf2_sample_voice_cache_update_handler(IpatchSF2VoiceCache *cache,
81         int *select_values,
82         GObject *cache_item,
83         GObject *item, GParamSpec *pspec,
84         const GValue *value,
85         IpatchSF2VoiceUpdate *updates,
86         guint max_updates);
87 static void ipatch_sf2_sample_real_set_name(IpatchSF2Sample *sample,
88         const char *name,
89         gboolean name_notify);
90 static void ipatch_sf2_sample_real_set_data(IpatchSF2Sample *sample,
91         IpatchSampleData *sampledata,
92         gboolean data_notify);
93 static void ipatch_sf2_sample_real_set_linked(IpatchSF2Sample *sample,
94         IpatchSF2Sample *linked,
95         gboolean linked_notify);
96 
97 /* cached parameter spec values for speed */
98 static GParamSpec *name_pspec;
99 static GParamSpec *sample_data_pspec;
100 static GParamSpec *linked_sample_pspec;
101 
G_DEFINE_TYPE_WITH_CODE(IpatchSF2Sample,ipatch_sf2_sample,IPATCH_TYPE_ITEM,G_IMPLEMENT_INTERFACE (IPATCH_TYPE_SAMPLE,ipatch_sf2_sample_iface_init))102 G_DEFINE_TYPE_WITH_CODE(IpatchSF2Sample, ipatch_sf2_sample, IPATCH_TYPE_ITEM,
103                         G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE,
104                                 ipatch_sf2_sample_iface_init))
105 
106 /* sample interface initialization */
107 static void
108 ipatch_sf2_sample_iface_init(IpatchSampleIface *sample_iface)
109 {
110     sample_iface->open = ipatch_sf2_sample_iface_open;
111 }
112 
113 static gboolean
ipatch_sf2_sample_iface_open(IpatchSampleHandle * handle,GError ** err)114 ipatch_sf2_sample_iface_open(IpatchSampleHandle *handle, GError **err)
115 {
116     IpatchSF2Sample *sample = IPATCH_SF2_SAMPLE(handle->sample);
117     g_return_val_if_fail(sample->sample_data != NULL, FALSE);
118     return (ipatch_sample_handle_cascade_open
119             (handle, (IpatchSample *)(sample->sample_data), err));
120 }
121 
122 static void
ipatch_sf2_sample_class_init(IpatchSF2SampleClass * klass)123 ipatch_sf2_sample_class_init(IpatchSF2SampleClass *klass)
124 {
125     GObjectClass *obj_class = G_OBJECT_CLASS(klass);
126     IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
127     GParamSpec *pspec;
128 
129     obj_class->finalize = ipatch_sf2_sample_finalize;
130     obj_class->get_property = ipatch_sf2_sample_get_property;
131 
132     /* we use the IpatchItem item_set_property method */
133     item_class->item_set_property = ipatch_sf2_sample_set_property;
134     item_class->copy = ipatch_sf2_sample_item_copy;
135     item_class->remove_full = ipatch_sf2_sample_item_remove_full;
136 
137     /* "name" property is used as the title */
138     g_object_class_override_property(obj_class, PROP_NAME, "title");
139 
140     name_pspec = g_param_spec_string("name", "Name", "Name",
141                                      NULL, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE);
142     ipatch_param_set(name_pspec,
143                      "string-max-length", IPATCH_SFONT_NAME_SIZE, /* max length */
144                      NULL);
145     g_object_class_install_property(obj_class, PROP_NAME, name_pspec);
146 
147     /* properties defined by IpatchSample interface */
148 
149     ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size");
150     ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format");
151 
152     pspec = ipatch_sample_install_property(obj_class, PROP_SAMPLE_RATE, "sample-rate");
153     pspec->flags |= IPATCH_PARAM_SYNTH;
154 
155     /* IpatchSF2Sample object's don't have a loop type field really */
156     ipatch_sample_install_property_readonly(obj_class, PROP_LOOP_TYPE, "loop-type");
157 
158     pspec = ipatch_sample_install_property(obj_class, PROP_LOOP_START, "loop-start");
159     pspec->flags |= IPATCH_PARAM_SYNTH | IPATCH_PARAM_SYNTH_REALTIME;
160 
161     pspec = ipatch_sample_install_property(obj_class, PROP_LOOP_END, "loop-end");
162     pspec->flags |= IPATCH_PARAM_SYNTH | IPATCH_PARAM_SYNTH_REALTIME;
163 
164     pspec = ipatch_sample_install_property(obj_class, PROP_ROOT_NOTE, "root-note");
165     pspec->flags |= IPATCH_PARAM_SYNTH;
166 
167     pspec = ipatch_sample_install_property(obj_class, PROP_FINE_TUNE, "fine-tune");
168     pspec->flags |= IPATCH_PARAM_SYNTH | IPATCH_PARAM_SYNTH_REALTIME;
169 
170     g_object_class_install_property(obj_class, PROP_CHANNEL,
171                                     g_param_spec_enum("channel", _("Channel orientation"), _("Channel orientation"),
172                                             IPATCH_TYPE_SF2_SAMPLE_CHANNEL,
173                                             IPATCH_SF2_SAMPLE_CHANNEL_MONO, G_PARAM_READWRITE));
174 
175     g_object_class_install_property(obj_class, PROP_ROM,
176                                     g_param_spec_boolean("rom", _("ROM sample flag"), _("ROM sample flag"),
177                                             FALSE, G_PARAM_READWRITE));
178 
179     sample_data_pspec
180         = ipatch_sample_install_property(obj_class, PROP_SAMPLE_DATA, "sample-data");
181 
182     linked_sample_pspec
183         = g_param_spec_object("linked-sample", _("Linked sample"),
184                               _("Stereo linked sample object"),
185                               IPATCH_TYPE_SF2_SAMPLE, G_PARAM_READWRITE);
186     g_object_class_install_property(obj_class, PROP_LINKED_SAMPLE,
187                                     linked_sample_pspec);
188 
189     /* install IpatchSF2VoiceCache update handler for real time effects */
190     ipatch_type_set(IPATCH_TYPE_SF2_SAMPLE, "sf2voice-update-func",
191                     ipatch_sf2_sample_voice_cache_update_handler, NULL);
192 }
193 
194 static void
ipatch_sf2_sample_init(IpatchSF2Sample * sample)195 ipatch_sf2_sample_init(IpatchSF2Sample *sample)
196 {
197     ipatch_sf2_sample_set_blank(sample);
198     sample->rate = IPATCH_SAMPLE_RATE_DEFAULT;
199     g_weak_ref_init(&sample->linked, NULL);
200 }
201 
202 static void
ipatch_sf2_sample_finalize(GObject * gobject)203 ipatch_sf2_sample_finalize(GObject *gobject)
204 {
205     IpatchSF2Sample *sample = IPATCH_SF2_SAMPLE(gobject);
206 
207     /* nothing should reference the sample after this, but we set
208        pointers to NULL to help catch invalid references. Locking of
209        sample is required since in reality all its children do
210        still hold references */
211 
212     ipatch_sf2_sample_real_set_data(sample, NULL, FALSE);
213 
214     IPATCH_ITEM_WLOCK(sample);
215 
216     g_weak_ref_clear(&sample->linked);
217 
218     g_free(sample->name);
219     sample->name = NULL;
220 
221     IPATCH_ITEM_WUNLOCK(sample);
222 
223     if(G_OBJECT_CLASS(ipatch_sf2_sample_parent_class)->finalize)
224     {
225         G_OBJECT_CLASS(ipatch_sf2_sample_parent_class)->finalize(gobject);
226     }
227 }
228 
229 static void
ipatch_sf2_sample_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)230 ipatch_sf2_sample_set_property(GObject *object, guint property_id,
231                                const GValue *value, GParamSpec *pspec)
232 {
233     IpatchSF2Sample *sample = IPATCH_SF2_SAMPLE(object);
234 
235     switch(property_id)
236     {
237     case PROP_NAME:
238         ipatch_sf2_sample_real_set_name(sample, g_value_get_string(value),
239                                         FALSE);	/* don't do name notify */
240         break;
241 
242     case PROP_SAMPLE_RATE:
243         IPATCH_ITEM_WLOCK(sample);
244         sample->rate = g_value_get_int(value);
245         IPATCH_ITEM_WUNLOCK(sample);
246         break;
247 
248     case PROP_LOOP_START:
249         IPATCH_ITEM_WLOCK(sample);
250         sample->loop_start = g_value_get_uint(value);
251         IPATCH_ITEM_WUNLOCK(sample);
252         break;
253 
254     case PROP_LOOP_END:
255         IPATCH_ITEM_WLOCK(sample);
256         sample->loop_end = g_value_get_uint(value);
257         IPATCH_ITEM_WUNLOCK(sample);
258         break;
259 
260     case PROP_ROOT_NOTE:
261         IPATCH_ITEM_WLOCK(sample);
262         sample->root_note = g_value_get_int(value);
263         IPATCH_ITEM_WUNLOCK(sample);
264         break;
265 
266     case PROP_FINE_TUNE:
267         IPATCH_ITEM_WLOCK(sample);
268         sample->fine_tune = g_value_get_int(value);
269         IPATCH_ITEM_WUNLOCK(sample);
270         break;
271 
272     case PROP_CHANNEL:
273         sample->channel = g_value_get_enum(value);
274         break;
275 
276     case PROP_ROM:
277         ipatch_item_set_flags((IpatchItem *)object,
278                               g_value_get_boolean(value) << IPATCH_SF2_SAMPLE_FLAG_ROM);
279         break;
280 
281     case PROP_SAMPLE_DATA:
282         ipatch_sf2_sample_real_set_data(sample, (IpatchSampleData *)
283                                         g_value_get_object(value), FALSE);
284         break;
285 
286     case PROP_LINKED_SAMPLE:
287         ipatch_sf2_sample_real_set_linked(sample, (IpatchSF2Sample *)
288                                           g_value_get_object(value), FALSE);
289         break;
290 
291     default:
292         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
293         return;
294     }
295 }
296 
297 static void
ipatch_sf2_sample_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)298 ipatch_sf2_sample_get_property(GObject *object, guint property_id,
299                                GValue *value, GParamSpec *pspec)
300 {
301     IpatchSF2Sample *sample;
302 
303     g_return_if_fail(IPATCH_IS_SF2_SAMPLE(object));
304     sample = IPATCH_SF2_SAMPLE(object);
305 
306     switch(property_id)
307     {
308     case PROP_NAME:
309         g_value_take_string(value, ipatch_sf2_sample_get_name(sample));
310         break;
311 
312     case PROP_SAMPLE_SIZE:
313         g_return_if_fail(sample->sample_data != NULL);
314         g_value_set_uint(value, ipatch_sample_get_size((IpatchSample *)(sample->sample_data), NULL));
315         break;
316 
317     case PROP_SAMPLE_FORMAT:
318         g_return_if_fail(sample->sample_data != NULL);
319         g_value_set_int(value, ipatch_sample_get_format((IpatchSample *)(sample->sample_data)));
320         break;
321 
322     case PROP_SAMPLE_RATE:
323         IPATCH_ITEM_RLOCK(sample);
324         g_value_set_int(value, sample->rate);
325         IPATCH_ITEM_RUNLOCK(sample);
326         break;
327 
328     case PROP_LOOP_TYPE:  /* IpatchSF2Sample objects don't have loop type, just use normal loop */
329         g_value_set_enum(value, IPATCH_SAMPLE_LOOP_STANDARD);
330         break;
331 
332     case PROP_LOOP_START:
333         IPATCH_ITEM_RLOCK(sample);
334         g_value_set_uint(value, sample->loop_start);
335         IPATCH_ITEM_RUNLOCK(sample);
336         break;
337 
338     case PROP_LOOP_END:
339         IPATCH_ITEM_RLOCK(sample);
340         g_value_set_uint(value, sample->loop_end);
341         IPATCH_ITEM_RUNLOCK(sample);
342         break;
343 
344     case PROP_ROOT_NOTE:
345         g_value_set_int(value, sample->root_note);
346         break;
347 
348     case PROP_FINE_TUNE:
349         g_value_set_int(value, sample->fine_tune);
350         break;
351 
352     case PROP_CHANNEL:
353         g_value_set_enum(value, sample->channel);
354         break;
355 
356     case PROP_ROM:
357         g_value_set_boolean(value, (ipatch_item_get_flags((IpatchItem *)object)
358                                     & IPATCH_SF2_SAMPLE_FLAG_ROM) != 0);
359         break;
360 
361     case PROP_SAMPLE_DATA:
362         g_value_take_object(value, ipatch_sf2_sample_get_data(sample));
363         break;
364 
365     case PROP_LINKED_SAMPLE:
366         g_value_take_object(value, ipatch_sf2_sample_get_linked(sample));
367         break;
368 
369     default:
370         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
371         break;
372     }
373 }
374 
375 static void
ipatch_sf2_sample_item_copy(IpatchItem * dest,IpatchItem * src,IpatchItemCopyLinkFunc link_func,gpointer user_data)376 ipatch_sf2_sample_item_copy(IpatchItem *dest, IpatchItem *src,
377                             IpatchItemCopyLinkFunc link_func,
378                             gpointer user_data)
379 {
380     IpatchSF2Sample *src_sam, *dest_sam;
381     IpatchItem *linked, *src_linked;
382 
383     src_sam = IPATCH_SF2_SAMPLE(src);
384     dest_sam = IPATCH_SF2_SAMPLE(dest);
385 
386     IPATCH_ITEM_RLOCK(src_sam);
387 
388     ipatch_sf2_sample_set_data(dest_sam, src_sam->sample_data);
389 
390     dest_sam->name = g_strdup(src_sam->name);
391     dest_sam->rate = src_sam->rate;
392     dest_sam->loop_start = src_sam->loop_start;
393     dest_sam->loop_end = src_sam->loop_end;
394     dest_sam->root_note = src_sam->root_note;
395     dest_sam->fine_tune = src_sam->fine_tune;
396     dest_sam->channel = src_sam->channel;
397 
398     if(ipatch_item_get_flags(src_sam) & IPATCH_SF2_SAMPLE_FLAG_ROM)
399     {
400         ipatch_item_set_flags(dest_sam, IPATCH_SF2_SAMPLE_FLAG_ROM);
401     }
402 
403     src_linked = (IpatchItem *)ipatch_sf2_sample_get_linked(src_sam);     // ++ ref src linked sample
404 
405     if(src_linked)
406     {
407         linked = IPATCH_ITEM_COPY_LINK_FUNC(dest, src_linked, link_func, user_data);
408         g_object_unref(src_linked);                                         // -- unref src linked sample
409 
410         if(linked)
411         {
412             ipatch_sf2_sample_set_linked(dest_sam, IPATCH_SF2_SAMPLE(linked));
413         }
414     }
415 
416     IPATCH_ITEM_RUNLOCK(src_sam);
417 }
418 
419 static void
ipatch_sf2_sample_item_remove_full(IpatchItem * item,gboolean full)420 ipatch_sf2_sample_item_remove_full(IpatchItem *item, gboolean full)
421 {
422     IpatchList *list;
423     IpatchSF2Sample *linked;
424     IpatchItem *zitem, *temp;
425     IpatchIter iter;
426 
427     list = ipatch_sf2_get_zone_references(item);	/* ++ ref zone list */
428     ipatch_list_init_iter(list, &iter);
429     zitem = ipatch_item_first(&iter);
430 
431     while(zitem)
432     {
433         temp = zitem;
434         zitem = ipatch_item_next(&iter);
435         ipatch_item_remove(temp);
436     }
437 
438     g_object_unref(list);	/* -- unref list */
439 
440     linked = ipatch_sf2_sample_get_linked(IPATCH_SF2_SAMPLE(item));   /* ++ ref */
441 
442     if(linked)
443     {
444         ipatch_sf2_sample_set_linked(linked, NULL);  /* clear link to item */
445         g_object_unref(linked);	/* -- unref linked sample */
446 
447         if(full)
448         {
449             ipatch_sf2_sample_set_linked(IPATCH_SF2_SAMPLE(item), NULL);
450         }
451     }
452 
453     if(full)
454     {
455         ipatch_sf2_sample_set_data(IPATCH_SF2_SAMPLE(item), NULL);
456     }
457 
458     if(IPATCH_ITEM_CLASS(ipatch_sf2_sample_parent_class)->remove_full)
459     {
460         IPATCH_ITEM_CLASS(ipatch_sf2_sample_parent_class)->remove_full(item, full);
461     }
462 }
463 
464 /* IpatchSF2VoiceCache update function for realtime effects */
465 static int
ipatch_sf2_sample_voice_cache_update_handler(IpatchSF2VoiceCache * cache,int * select_values,GObject * cache_item,GObject * item,GParamSpec * pspec,const GValue * value,IpatchSF2VoiceUpdate * updates,guint max_updates)466 ipatch_sf2_sample_voice_cache_update_handler(IpatchSF2VoiceCache *cache,
467         int *select_values,
468         GObject *cache_item,
469         GObject *item, GParamSpec *pspec,
470         const GValue *value,
471         IpatchSF2VoiceUpdate *updates,
472         guint max_updates)
473 {
474     IpatchSF2Voice *voice;
475     guint8 genid, genid2 = 255;
476     gint16 val = 0, val2 = 0;
477     int ival;
478 
479     g_return_val_if_fail(cache->voices->len > 0, 0);
480 
481     voice = IPATCH_SF2_VOICE_CACHE_GET_VOICE(cache, 0);
482 
483     switch(IPATCH_PARAM_SPEC_ID(pspec))
484     {
485     case PROP_LOOP_START:
486         genid = IPATCH_SF2_GEN_SAMPLE_LOOP_START;
487         genid2 = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START;
488         ival = (int)g_value_get_uint(value) - voice->loop_start;
489         val = ival % 32768;
490         val2 = ival / 32768;
491         break;
492 
493     case PROP_LOOP_END:
494         genid = IPATCH_SF2_GEN_SAMPLE_LOOP_END;
495         genid2 = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END;
496         ival = (int)g_value_get_uint(value) - voice->loop_end;
497         val = ival % 32768;
498         val2 = ival / 32768;
499         break;
500 
501     case PROP_FINE_TUNE:
502         genid = IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE;
503         ival = g_value_get_int(value);
504         break;
505 
506     default:
507         return (0);
508     }
509 
510     updates->voice = 0;
511     updates->genid = genid;
512     updates->ival = val;
513 
514     if(genid2 != 255 && max_updates >= 2)
515     {
516         updates[1].voice = 0;
517         updates[1].genid = genid2;
518         updates[1].ival = val2;
519         return (2);
520     }
521     else
522     {
523         return (1);
524     }
525 }
526 
527 /**
528  * ipatch_sf2_sample_new:
529  *
530  * Create a new SoundFont sample object.
531  *
532  * Returns: New SoundFont sample with a reference count of 1. Caller
533  * owns the reference and removing it will destroy the item, unless another
534  * reference is added (if its parented for example).
535  */
536 IpatchSF2Sample *
ipatch_sf2_sample_new(void)537 ipatch_sf2_sample_new(void)
538 {
539     return (IPATCH_SF2_SAMPLE(g_object_new(IPATCH_TYPE_SF2_SAMPLE, NULL)));
540 }
541 
542 /**
543  * ipatch_sf2_sample_first: (skip)
544  * @iter: Patch item iterator containing #IpatchSF2Sample items
545  *
546  * Gets the first item in a sample iterator. A convenience wrapper for
547  * ipatch_iter_first().
548  *
549  * Returns: The first sample in @iter or %NULL if empty.
550  */
551 IpatchSF2Sample *
ipatch_sf2_sample_first(IpatchIter * iter)552 ipatch_sf2_sample_first(IpatchIter *iter)
553 {
554     GObject *obj;
555     g_return_val_if_fail(iter != NULL, NULL);
556 
557     obj = ipatch_iter_first(iter);
558 
559     if(obj)
560     {
561         return (IPATCH_SF2_SAMPLE(obj));
562     }
563     else
564     {
565         return (NULL);
566     }
567 }
568 
569 /**
570  * ipatch_sf2_sample_next: (skip)
571  * @iter: Patch item iterator containing #IpatchSF2Sample items
572  *
573  * Gets the next item in a sample iterator. A convenience wrapper for
574  * ipatch_iter_next().
575  *
576  * Returns: The next sample in @iter or %NULL if at the end of the list.
577  */
578 IpatchSF2Sample *
ipatch_sf2_sample_next(IpatchIter * iter)579 ipatch_sf2_sample_next(IpatchIter *iter)
580 {
581     GObject *obj;
582     g_return_val_if_fail(iter != NULL, NULL);
583 
584     obj = ipatch_iter_next(iter);
585 
586     if(obj)
587     {
588         return (IPATCH_SF2_SAMPLE(obj));
589     }
590     else
591     {
592         return (NULL);
593     }
594 }
595 
596 /**
597  * ipatch_sf2_sample_set_name:
598  * @sample: Sample to set name of
599  * @name: (nullable): Value to set name to
600  *
601  * Sets the name of a SoundFont sample.
602  */
603 void
ipatch_sf2_sample_set_name(IpatchSF2Sample * sample,const char * name)604 ipatch_sf2_sample_set_name(IpatchSF2Sample *sample, const char *name)
605 {
606     g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample));
607     ipatch_sf2_sample_real_set_name(sample, name, TRUE);
608 }
609 
610 /* also called from item_set_property method so "name_notify" can be used to
611    stop double emission of name notify */
612 static void
ipatch_sf2_sample_real_set_name(IpatchSF2Sample * sample,const char * name,gboolean name_notify)613 ipatch_sf2_sample_real_set_name(IpatchSF2Sample *sample, const char *name,
614                                 gboolean name_notify)
615 {
616     GValue oldval = { 0 }, newval = { 0 };
617     char *newname;
618 
619     g_value_init(&oldval, G_TYPE_STRING);
620 
621     newname = g_strdup(name);
622 
623     IPATCH_ITEM_WLOCK(sample);
624     g_value_take_string(&oldval, sample->name);
625     sample->name = newname;
626     IPATCH_ITEM_WUNLOCK(sample);
627 
628     g_value_init(&newval, G_TYPE_STRING);
629     g_value_set_static_string(&newval, name);
630 
631     ipatch_item_prop_notify((IpatchItem *)sample, ipatch_item_pspec_title,
632                             &newval, &oldval);
633 
634     if(name_notify)
635     {
636         ipatch_item_prop_notify((IpatchItem *)sample, name_pspec, &newval, &oldval);
637     }
638 
639     g_value_unset(&oldval);
640     g_value_unset(&newval);
641 }
642 
643 /**
644  * ipatch_sf2_sample_get_name:
645  * @sample: Sample to get name of
646  *
647  * Gets the name of a SoundFont sample.
648  *
649  * Returns: (nullable) (transfer full): Name of sample or %NULL if not set.
650  *   String value should be freed when finished with it.
651  */
652 char *
ipatch_sf2_sample_get_name(IpatchSF2Sample * sample)653 ipatch_sf2_sample_get_name(IpatchSF2Sample *sample)
654 {
655     char *name = NULL;
656 
657     g_return_val_if_fail(IPATCH_IS_SF2_SAMPLE(sample), NULL);
658 
659     IPATCH_ITEM_RLOCK(sample);
660 
661     if(sample->name)
662     {
663         name = g_strdup(sample->name);
664     }
665 
666     IPATCH_ITEM_RUNLOCK(sample);
667 
668     return (name);
669 }
670 
671 /**
672  * ipatch_sf2_sample_set_data:
673  * @sample: Sample to set sample data of
674  * @sampledata: (nullable): Sample data to set sample to
675  *
676  * Set a sample's sample data object.
677  */
678 void
ipatch_sf2_sample_set_data(IpatchSF2Sample * sample,IpatchSampleData * sampledata)679 ipatch_sf2_sample_set_data(IpatchSF2Sample *sample,
680                            IpatchSampleData *sampledata)
681 {
682     g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample));
683     g_return_if_fail(!sampledata || IPATCH_IS_SAMPLE_DATA(sampledata));
684 
685     ipatch_sf2_sample_real_set_data(sample, sampledata, TRUE);
686 }
687 
688 /* the actual setting of sample data, user routine does a g_object_notify */
689 static void
ipatch_sf2_sample_real_set_data(IpatchSF2Sample * sample,IpatchSampleData * sampledata,gboolean data_notify)690 ipatch_sf2_sample_real_set_data(IpatchSF2Sample *sample,
691                                 IpatchSampleData *sampledata,
692                                 gboolean data_notify)
693 {
694     GValue oldval = { 0 }, newval = { 0 };
695     IpatchSampleData *old_sampledata;
696 
697     if(sampledata)
698     {
699         g_object_ref(sampledata);
700         ipatch_sample_data_used(sampledata);    /* ++ inc use count */
701     }
702 
703     IPATCH_ITEM_WLOCK(sample);
704     old_sampledata = sample->sample_data;
705     sample->sample_data = sampledata;
706     IPATCH_ITEM_WUNLOCK(sample);
707 
708     if(old_sampledata)
709     {
710         ipatch_sample_data_unused(old_sampledata);    // -- dec use count
711     }
712 
713     if(data_notify)
714     {
715         g_value_init(&newval, IPATCH_TYPE_SAMPLE_DATA);
716         g_value_set_object(&newval, sampledata);
717 
718         g_value_init(&oldval, IPATCH_TYPE_SAMPLE_DATA);
719         g_value_take_object(&oldval, old_sampledata);
720 
721         ipatch_item_prop_notify((IpatchItem *)sample, sample_data_pspec, &newval, &oldval);
722         g_value_unset(&newval);
723         g_value_unset(&oldval);
724     }
725     else if(old_sampledata)
726     {
727         g_object_unref(old_sampledata);    // -- unref
728     }
729 }
730 
731 /**
732  * ipatch_sf2_sample_get_data:
733  * @sample: Sample to get sample data from
734  *
735  * Get the #IpatchSampleData item of a sample. Sample data item is referenced
736  * before returning and caller is responsible for unreferencing it with
737  * g_object_unref() when finished with it.
738  *
739  * Returns: (transfer full): Sample data object of sample or %NULL if none. Remember to
740  * unreference with g_object_unref() when finished with it.
741  */
742 IpatchSampleData *
ipatch_sf2_sample_get_data(IpatchSF2Sample * sample)743 ipatch_sf2_sample_get_data(IpatchSF2Sample *sample)
744 {
745     IpatchSampleData *sampledata;
746 
747     g_return_val_if_fail(IPATCH_IS_SF2_SAMPLE(sample), NULL);
748 
749     IPATCH_ITEM_RLOCK(sample);
750     sampledata = sample->sample_data;
751 
752     if(sampledata)
753     {
754         g_object_ref(sampledata);
755     }
756 
757     IPATCH_ITEM_RUNLOCK(sample);
758 
759     return (sampledata);
760 }
761 
762 /**
763  * ipatch_sf2_sample_peek_data: (skip)
764  * @sample: Sample to get sample data from
765  *
766  * Get the #IpatchSampleData item of a sample. Like
767  * ipatch_sf2_sample_get_data() but sample data object is not referenced.
768  * This function should only be used if a reference of the sample data object
769  * is ensured or only the pointer value is of importance.
770  *
771  * Returns: (transfer none): Sample data object of sample or %NULL if none.
772  * Remember that a reference is NOT added.
773  */
774 IpatchSampleData *
ipatch_sf2_sample_peek_data(IpatchSF2Sample * sample)775 ipatch_sf2_sample_peek_data(IpatchSF2Sample *sample)
776 {
777     IpatchSampleData *sampledata;
778 
779     g_return_val_if_fail(IPATCH_IS_SF2_SAMPLE(sample), NULL);
780 
781     IPATCH_ITEM_RLOCK(sample);
782     sampledata = sample->sample_data;
783     IPATCH_ITEM_RUNLOCK(sample);
784 
785     return (sampledata);
786 }
787 
788 /**
789  * ipatch_sf2_sample_set_linked:
790  * @sample: Sample to set linked sample of
791  * @linked: (nullable): Sample that is stereo linked to @sample or %NULL to unset.
792  *
793  * Sets the stereo linked sample of a sample item.
794  */
795 void
ipatch_sf2_sample_set_linked(IpatchSF2Sample * sample,IpatchSF2Sample * linked)796 ipatch_sf2_sample_set_linked(IpatchSF2Sample *sample,
797                              IpatchSF2Sample *linked)
798 {
799     g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample));
800     g_return_if_fail(!linked || IPATCH_IS_SF2_SAMPLE(linked));
801 
802     ipatch_sf2_sample_real_set_linked(sample, linked, TRUE);
803 }
804 
805 /* real set linked sample */
806 static void
ipatch_sf2_sample_real_set_linked(IpatchSF2Sample * sample,IpatchSF2Sample * linked,gboolean linked_notify)807 ipatch_sf2_sample_real_set_linked(IpatchSF2Sample *sample,
808                                   IpatchSF2Sample *linked,
809                                   gboolean linked_notify)
810 {
811     GValue oldval = { 0 }, newval = { 0 };
812     GObject *old_linked;
813 
814     IPATCH_ITEM_WLOCK(sample);
815 
816     if(linked_notify)
817     {
818         old_linked = g_weak_ref_get(&sample->linked);    // ++ ref old linked item
819     }
820 
821     g_weak_ref_set(&sample->linked, linked);
822     IPATCH_ITEM_WUNLOCK(sample);
823 
824     if(linked_notify)
825     {
826         g_value_init(&oldval, IPATCH_TYPE_SF2_SAMPLE);
827         g_value_take_object(&oldval, old_linked);                         // !! value takes over old linked item
828 
829         g_value_init(&newval, IPATCH_TYPE_SF2_SAMPLE);
830         g_value_set_object(&newval, linked);
831 
832         ipatch_item_prop_notify((IpatchItem *)sample, sample_data_pspec,
833                                 &newval, &oldval);
834         g_value_unset(&newval);
835         g_value_unset(&oldval);
836     }
837 }
838 
839 /**
840  * ipatch_sf2_sample_get_linked:
841  * @sample: Sample to get linked sample from
842  *
843  * Get the stereo linked sample from @sample. If a sample is returned the
844  * caller owns a reference and should unref it with g_object_unref()
845  * when finished with it.
846  *
847  * Returns: (transfer full): The linked stereo sample or %NULL if no linked sample. Remember to
848  * unref the returned sample with g_object_unref() when finished with it.
849  */
850 IpatchSF2Sample *
ipatch_sf2_sample_get_linked(IpatchSF2Sample * sample)851 ipatch_sf2_sample_get_linked(IpatchSF2Sample *sample)
852 {
853     IpatchSF2Sample *linked;
854 
855     IPATCH_ITEM_RLOCK(sample);
856     linked = g_weak_ref_get(&sample->linked);     // ++ ref linked
857     IPATCH_ITEM_RUNLOCK(sample);
858 
859     return (linked);      // !! caller takes over reference
860 }
861 
862 /**
863  * ipatch_sf2_sample_peek_linked: (skip)
864  * @sample: Sample to get linked sample from
865  *
866  * Get the stereo linked sample from @sample. Like
867  * ipatch_sf2_sample_get_linked() but sample object is not referenced.
868  * This function should only be used if a reference of the sample object
869  * is ensured or only the pointer value is of importance.
870  *
871  * Returns: (transfer none): Linked sample object of sample or %NULL if none.
872  * Remember that a reference is NOT added.
873  */
874 IpatchSF2Sample *
ipatch_sf2_sample_peek_linked(IpatchSF2Sample * sample)875 ipatch_sf2_sample_peek_linked(IpatchSF2Sample *sample)
876 {
877     IpatchSF2Sample *linked;
878 
879     g_return_val_if_fail(IPATCH_IS_SF2_SAMPLE(sample), NULL);
880 
881     IPATCH_ITEM_RLOCK(sample);
882     linked = g_weak_ref_get(&sample->linked);     // ++ ref linked
883     IPATCH_ITEM_RUNLOCK(sample);
884 
885     if(linked)
886     {
887         g_object_unref(linked);    // -- unref linked
888     }
889 
890     return (linked);
891 }
892 
893 /**
894  * ipatch_sf2_sample_set_blank:
895  * @sample: Sample to set to blank sample data
896  *
897  * Set the sample data of a sample item to blank data.
898  */
899 void
ipatch_sf2_sample_set_blank(IpatchSF2Sample * sample)900 ipatch_sf2_sample_set_blank(IpatchSF2Sample *sample)
901 {
902     IpatchSampleData *sampledata;
903 
904     g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample));
905 
906     sampledata = ipatch_sample_data_get_blank();
907     ipatch_item_set_atomic(sample,
908                            "sample-data", sampledata,
909                            "loop-start", 8,
910                            "loop-end", 40,
911                            "root-note", 60,
912                            "fine-tune", 0,
913                            "channel", IPATCH_SF2_SAMPLE_CHANNEL_MONO,
914                            "rom", FALSE,
915                            "linked-sample", NULL,
916                            NULL);
917     g_object_unref(sampledata);
918 }
919