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