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: IpatchDLS2Region
22 * @short_description: DLS region object
23 * @see_also: #IpatchDLSInst
24 * @stability: Stable
25 *
26 * DLS regions are child items of #IpatchDLSInst objects and define how an
27 * individual audio sample is synthesized in an instrument.
28 */
29 #include <stdarg.h>
30 #include <glib.h>
31 #include <glib-object.h>
32 #include "IpatchDLS2Region.h"
33 #include "IpatchGigRegion.h"
34 #include "IpatchRange.h"
35 #include "IpatchSample.h"
36 #include "IpatchTypeProp.h"
37 #include "ipatch_priv.h"
38
39 enum
40 {
41 PROP_0,
42
43 PROP_TITLE,
44
45 PROP_NOTE_RANGE,
46 PROP_VELOCITY_RANGE,
47
48 PROP_KEY_GROUP,
49 PROP_LAYER_GROUP,
50 PROP_PHASE_GROUP,
51 PROP_CHANNEL,
52 PROP_LINK_ITEM,
53 PROP_SAMPLE_INFO_OVERRIDE, /* sample info override boolean */
54
55 /* IpatchItem flags (no one needs to know that though) */
56 PROP_SELF_NON_EXCLUSIVE,
57 PROP_PHASE_MASTER,
58 PROP_MULTI_CHANNEL,
59
60 /* IpatchSample interface properties */
61 PROP_SAMPLE_SIZE,
62 PROP_SAMPLE_FORMAT,
63 PROP_SAMPLE_RATE,
64 PROP_SAMPLE_DATA
65 };
66
67 enum
68 {
69 SET_CONN,
70 UNSET_CONN,
71 LAST_SIGNAL
72 };
73
74 static void ipatch_dls2_region_sample_iface_init(IpatchSampleIface *sample_iface);
75 static gboolean ipatch_dls2_region_sample_iface_open(IpatchSampleHandle *handle,
76 GError **err);
77 static void ipatch_dls2_region_class_init(IpatchDLS2RegionClass *klass);
78 static void ipatch_dls2_region_init(IpatchDLS2Region *region);
79 static void ipatch_dls2_region_finalize(GObject *gobject);
80 static void ipatch_dls2_region_get_title(IpatchDLS2Region *region,
81 GValue *value);
82 static void ipatch_dls2_region_set_property(GObject *object,
83 guint property_id,
84 const GValue *value,
85 GParamSpec *pspec);
86 static void ipatch_dls2_region_get_property(GObject *object,
87 guint property_id, GValue *value,
88 GParamSpec *pspec);
89 static void ipatch_dls2_region_item_copy(IpatchItem *dest, IpatchItem *src,
90 IpatchItemCopyLinkFunc link_func,
91 gpointer user_data);
92 static void ipatch_dls2_region_item_remove_full(IpatchItem *item, gboolean full);
93 static void ipatch_dls2_region_real_set_sample(IpatchDLS2Region *region,
94 IpatchDLS2Sample *sample,
95 gboolean sample_notify);
96 static void ipatch_dls2_region_get_sample_info(IpatchDLS2Region *region,
97 IpatchDLS2SampleInfo *info);
98
99 /* cached param specs to speed up prop notifies */
100 static GParamSpec *link_item_pspec;
101
G_DEFINE_TYPE_WITH_CODE(IpatchDLS2Region,ipatch_dls2_region,IPATCH_TYPE_ITEM,G_IMPLEMENT_INTERFACE (IPATCH_TYPE_SAMPLE,ipatch_dls2_region_sample_iface_init))102 G_DEFINE_TYPE_WITH_CODE(IpatchDLS2Region, ipatch_dls2_region, IPATCH_TYPE_ITEM,
103 G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE,
104 ipatch_dls2_region_sample_iface_init))
105
106 /* sample interface initialization */
107 static void
108 ipatch_dls2_region_sample_iface_init(IpatchSampleIface *sample_iface)
109 {
110 sample_iface->open = ipatch_dls2_region_sample_iface_open;
111 sample_iface->loop_types = ipatch_sample_loop_types_standard_release;
112 }
113
114 static gboolean
ipatch_dls2_region_sample_iface_open(IpatchSampleHandle * handle,GError ** err)115 ipatch_dls2_region_sample_iface_open(IpatchSampleHandle *handle, GError **err)
116 {
117 IpatchDLS2Region *region = IPATCH_DLS2_REGION(handle->sample);
118 g_return_val_if_fail(region->sample != NULL, FALSE);
119 return (ipatch_sample_handle_cascade_open(handle, IPATCH_SAMPLE(region->sample), err));
120 }
121
122 static void
ipatch_dls2_region_class_init(IpatchDLS2RegionClass * klass)123 ipatch_dls2_region_class_init(IpatchDLS2RegionClass *klass)
124 {
125 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
126 IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
127
128 obj_class->finalize = ipatch_dls2_region_finalize;
129 obj_class->get_property = ipatch_dls2_region_get_property;
130
131 item_class->item_set_property = ipatch_dls2_region_set_property;
132 item_class->copy = ipatch_dls2_region_item_copy;
133 item_class->remove_full = ipatch_dls2_region_item_remove_full;
134
135 g_object_class_override_property(obj_class, PROP_TITLE, "title");
136
137 g_object_class_install_property(obj_class, PROP_NOTE_RANGE,
138 ipatch_param_spec_range("note-range", _("Note range"),
139 _("MIDI note range"),
140 0, 127, 0, 127,
141 G_PARAM_READWRITE));
142 g_object_class_install_property(obj_class, PROP_VELOCITY_RANGE,
143 ipatch_param_spec_range("velocity-range", _("Velocity range"),
144 _("MIDI velocity range"),
145 0, 127, 0, 127,
146 G_PARAM_READWRITE));
147
148 g_object_class_install_property(obj_class, PROP_KEY_GROUP,
149 g_param_spec_int("key-group", _("Key group"),
150 _("Percussion key group"),
151 0, 15, 0,
152 G_PARAM_READWRITE));
153 g_object_class_install_property(obj_class, PROP_LAYER_GROUP,
154 g_param_spec_int("layer-group", _("Layer group"),
155 _("Layer group"),
156 0, G_MAXUSHORT, 0,
157 G_PARAM_READWRITE));
158 g_object_class_install_property(obj_class, PROP_PHASE_GROUP,
159 g_param_spec_int("phase-group", _("Phase group"),
160 _("Phase locked sample group"),
161 0, G_MAXUSHORT, 0,
162 G_PARAM_READWRITE));
163 g_object_class_install_property(obj_class, PROP_CHANNEL,
164 g_param_spec_int("channel", _("Channel"),
165 _("DLS audio channel identifier"),
166 0, 0x03FFFF, 0,
167 G_PARAM_READWRITE));
168
169 link_item_pspec =
170 g_param_spec_object("link-item", _("Link item"), _("Link item"),
171 IPATCH_TYPE_DLS2_SAMPLE, G_PARAM_READWRITE);
172 g_object_class_install_property(obj_class, PROP_LINK_ITEM, link_item_pspec);
173
174 g_object_class_install_property(obj_class, PROP_SAMPLE_INFO_OVERRIDE,
175 g_param_spec_boolean("sample-info-override",
176 _("Override sample info"),
177 _("Override sample info"),
178 FALSE, G_PARAM_READWRITE));
179
180 g_object_class_install_property(obj_class, PROP_SELF_NON_EXCLUSIVE,
181 g_param_spec_boolean("self-non-exclusive",
182 _("Non exclusive"),
183 _("Self non exclusive"),
184 FALSE,
185 G_PARAM_READWRITE));
186 g_object_class_install_property(obj_class, PROP_PHASE_MASTER,
187 g_param_spec_boolean("phase-master",
188 _("Phase master"),
189 _("Multi channel phase lock master"),
190 FALSE,
191 G_PARAM_READWRITE));
192 g_object_class_install_property(obj_class, PROP_MULTI_CHANNEL,
193 g_param_spec_boolean("multi-channel",
194 _("Multi channel"),
195 _("Multi channel"),
196 FALSE,
197 G_PARAM_READWRITE));
198
199 /* IpatchSample interface properties */
200 ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size");
201 ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format");
202 ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_RATE, "sample-rate");
203 ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_DATA, "sample-data");
204
205 ipatch_dls2_info_install_class_properties(obj_class);
206 ipatch_dls2_sample_info_install_class_properties(obj_class);
207 }
208
209 static void
ipatch_dls2_region_init(IpatchDLS2Region * region)210 ipatch_dls2_region_init(IpatchDLS2Region *region)
211 {
212 region->note_range_low = 0;
213 region->note_range_high = 127;
214 region->velocity_range_low = 0;
215 region->velocity_range_high = 127;
216 region->key_group = 0;
217 region->layer_group = 0;
218 region->phase_group = 0;
219 region->channel = 0;
220 region->info = NULL;
221 region->sample_info = NULL;
222 region->sample = NULL;
223 region->conns = NULL;
224 }
225
226 static void
ipatch_dls2_region_finalize(GObject * gobject)227 ipatch_dls2_region_finalize(GObject *gobject)
228 {
229 IpatchDLS2Region *region = IPATCH_DLS2_REGION(gobject);
230 GSList *p;
231
232 IPATCH_ITEM_WLOCK(region);
233
234 if(region->sample_info)
235 {
236 ipatch_dls2_sample_info_free(region->sample_info);
237 region->sample_info = NULL;
238 }
239
240 if(region->sample)
241 {
242 g_object_unref(region->sample);
243 region->sample = NULL;
244 }
245
246 p = region->conns;
247
248 while(p)
249 {
250 ipatch_dls2_conn_free((IpatchDLS2Conn *)(p->data));
251 p = g_slist_delete_link(p, p);
252 }
253
254 region->conns = NULL;
255
256 IPATCH_ITEM_WUNLOCK(region);
257
258 if(G_OBJECT_CLASS(ipatch_dls2_region_parent_class)->finalize)
259 {
260 G_OBJECT_CLASS(ipatch_dls2_region_parent_class)->finalize(gobject);
261 }
262 }
263
264 static void
ipatch_dls2_region_get_title(IpatchDLS2Region * region,GValue * value)265 ipatch_dls2_region_get_title(IpatchDLS2Region *region, GValue *value)
266 {
267 IpatchDLS2Sample *sample;
268 char *s = NULL;
269
270 sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */
271
272 if(sample)
273 {
274 g_object_get(sample, "name", &s, NULL);
275 g_object_unref(sample); /* -- unref sample */
276 }
277
278 g_value_take_string(value, s);
279 }
280
281 static void
ipatch_dls2_region_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)282 ipatch_dls2_region_set_property(GObject *object, guint property_id,
283 const GValue *value, GParamSpec *pspec)
284 {
285 IpatchDLS2Region *region = IPATCH_DLS2_REGION(object);
286 IpatchDLS2SampleInfo saminfo = IPATCH_DLS2_SAMPLE_INFO_INIT;
287 IpatchDLS2SampleInfo oldinfo, newinfo;
288 IpatchRange *range;
289 gboolean is_samprop;
290 gboolean retval;
291
292 switch(property_id)
293 {
294 case PROP_NOTE_RANGE:
295 range = ipatch_value_get_range(value);
296
297 if(range)
298 {
299 IPATCH_ITEM_WLOCK(region);
300 region->note_range_low = range->low;
301 region->note_range_high = range->high;
302 IPATCH_ITEM_WUNLOCK(region);
303 }
304
305 break;
306
307 case PROP_VELOCITY_RANGE:
308 range = ipatch_value_get_range(value);
309
310 if(range)
311 {
312 IPATCH_ITEM_WLOCK(region);
313 region->velocity_range_low = range->low;
314 region->velocity_range_high = range->high;
315 IPATCH_ITEM_WUNLOCK(region);
316 }
317
318 break;
319
320 case PROP_KEY_GROUP:
321 region->key_group = g_value_get_int(value);
322 break;
323
324 case PROP_LAYER_GROUP:
325 region->layer_group = g_value_get_int(value);
326 break;
327
328 case PROP_PHASE_GROUP:
329 region->phase_group = g_value_get_int(value);
330 break;
331
332 case PROP_CHANNEL:
333 region->channel = g_value_get_int(value);
334 break;
335
336 case PROP_LINK_ITEM:
337 ipatch_dls2_region_real_set_sample(region, IPATCH_DLS2_SAMPLE
338 (g_value_get_object(value)), FALSE);
339 break;
340
341 case PROP_SAMPLE_INFO_OVERRIDE:
342 ipatch_dls2_region_get_sample_info(region, &oldinfo);
343
344 if(g_value_get_boolean(value))
345 ipatch_item_set_flags((IpatchItem *)region,
346 IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE);
347 else
348 ipatch_item_clear_flags((IpatchItem *)region,
349 IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE);
350
351 ipatch_dls2_region_get_sample_info(region, &newinfo);
352 ipatch_dls2_sample_info_notify_changes((IpatchItem *)region, &newinfo,
353 &oldinfo);
354 break;
355
356 case PROP_SELF_NON_EXCLUSIVE:
357 if(g_value_get_boolean(value))
358 ipatch_item_set_flags(IPATCH_ITEM(object),
359 IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE);
360 else
361 ipatch_item_clear_flags(IPATCH_ITEM(object),
362 IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE);
363
364 break;
365
366 case PROP_PHASE_MASTER:
367 if(g_value_get_boolean(value))
368 ipatch_item_set_flags(IPATCH_ITEM(object),
369 IPATCH_DLS2_REGION_PHASE_MASTER);
370 else
371 ipatch_item_clear_flags(IPATCH_ITEM(object),
372 IPATCH_DLS2_REGION_PHASE_MASTER);
373
374 break;
375
376 case PROP_MULTI_CHANNEL:
377 if(g_value_get_boolean(value))
378 ipatch_item_set_flags(IPATCH_ITEM(object),
379 IPATCH_DLS2_REGION_MULTI_CHANNEL);
380 else
381 ipatch_item_clear_flags(IPATCH_ITEM(object),
382 IPATCH_DLS2_REGION_MULTI_CHANNEL);
383
384 break;
385
386 default:
387 is_samprop = ipatch_dls2_sample_info_is_property_id_valid(property_id);
388
389 /* check if region override info valid but override flag not set. If so
390 then copy sample info to static 'saminfo'. OK to test region without
391 locking it (worst that happens is default values get used). */
392 if(is_samprop && region->sample_info
393 && !(ipatch_item_get_flags(region) & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE))
394 {
395 ipatch_dls2_region_get_sample_info(region, &saminfo);
396 }
397
398 IPATCH_ITEM_WLOCK(region);
399
400 /* is override sample_info valid but override flag not set and it is
401 in fact a sample info property? - Copy values from sample
402 (or defaults) */
403 if(is_samprop && region->sample_info
404 && !(ipatch_item_get_flags(region) & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE))
405 {
406 *region->sample_info = saminfo;
407 }
408
409 retval = ipatch_dls2_sample_info_set_property(®ion->sample_info,
410 property_id, value);
411
412 if(retval) /* sample info set, set override flag */
413 {
414 ipatch_item_set_flags(region, IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE);
415 }
416 else
417 retval = ipatch_dls2_info_set_property(®ion->info,
418 property_id, value);
419
420 IPATCH_ITEM_WUNLOCK(region);
421
422 if(!retval)
423 {
424 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
425 return;
426 }
427
428 break;
429 }
430 }
431
432 static void
ipatch_dls2_region_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)433 ipatch_dls2_region_get_property(GObject *object, guint property_id,
434 GValue *value, GParamSpec *pspec)
435 {
436 IpatchDLS2Region *region = IPATCH_DLS2_REGION(object);
437 IpatchDLS2Sample *sample = NULL;
438 IpatchRange range;
439 gboolean bool, retval = 0;
440 gboolean get_from_sample = FALSE;
441
442 switch(property_id)
443 {
444 case PROP_TITLE:
445 ipatch_dls2_region_get_title(region, value);
446 break;
447
448 case PROP_NOTE_RANGE:
449 IPATCH_ITEM_RLOCK(region);
450 range.low = region->note_range_low;
451 range.high = region->note_range_high;
452 IPATCH_ITEM_RUNLOCK(region);
453
454 ipatch_value_set_range(value, &range);
455 break;
456
457 case PROP_VELOCITY_RANGE:
458 IPATCH_ITEM_RLOCK(region);
459 range.low = region->velocity_range_low;
460 range.high = region->velocity_range_high;
461 IPATCH_ITEM_RUNLOCK(region);
462
463 ipatch_value_set_range(value, &range);
464 break;
465
466 case PROP_KEY_GROUP:
467 g_value_set_int(value, region->key_group);
468 break;
469
470 case PROP_LAYER_GROUP:
471 g_value_set_int(value, region->layer_group);
472 break;
473
474 case PROP_PHASE_GROUP:
475 g_value_set_int(value, region->phase_group);
476 break;
477
478 case PROP_CHANNEL:
479 g_value_set_int(value, region->channel);
480 break;
481
482 case PROP_LINK_ITEM:
483 g_value_take_object(value, ipatch_dls2_region_get_sample(region));
484 break;
485
486 case PROP_SAMPLE_INFO_OVERRIDE:
487 g_value_set_boolean(value, (ipatch_item_get_flags((IpatchItem *)region)
488 & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE) != 0);
489 break;
490
491 case PROP_SELF_NON_EXCLUSIVE:
492 bool = (ipatch_item_get_flags(IPATCH_ITEM(object))
493 & IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE) > 0;
494 g_value_set_boolean(value, bool);
495 break;
496
497 case PROP_PHASE_MASTER:
498 bool = (ipatch_item_get_flags(IPATCH_ITEM(object))
499 & IPATCH_DLS2_REGION_PHASE_MASTER) > 0;
500 g_value_set_boolean(value, bool);
501 break;
502
503 case PROP_MULTI_CHANNEL:
504 bool = (ipatch_item_get_flags(IPATCH_ITEM(object))
505 & IPATCH_DLS2_REGION_MULTI_CHANNEL) > 0;
506 g_value_set_boolean(value, bool);
507 break;
508
509 case PROP_SAMPLE_SIZE:
510 sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */
511 g_return_if_fail(sample != NULL);
512 g_object_get_property((GObject *)sample, "sample-size", value);
513 g_object_unref(sample); /* -- unref sample */
514 break;
515
516 case PROP_SAMPLE_FORMAT:
517 sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */
518 g_return_if_fail(sample != NULL);
519 g_object_get_property((GObject *)sample, "sample-size", value);
520 g_object_unref(sample); /* -- unref sample */
521 break;
522
523 case PROP_SAMPLE_RATE:
524 sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */
525 g_return_if_fail(sample != NULL);
526 g_object_get_property((GObject *)sample, "sample-rate", value);
527 g_object_unref(sample); /* -- unref sample */
528 break;
529
530 case PROP_SAMPLE_DATA:
531 sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */
532 g_return_if_fail(sample != NULL);
533 g_object_get_property((GObject *)sample, "sample-data", value);
534 g_object_unref(sample); /* -- unref sample */
535 break;
536
537 default:
538 IPATCH_ITEM_RLOCK(region);
539
540 /* a sample info property? */
541 if(property_id >= IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID
542 && property_id < (IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID
543 + IPATCH_DLS2_SAMPLE_INFO_PROPERTY_COUNT))
544 {
545 if(ipatch_item_get_flags(region) & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE
546 && region->sample_info)
547 retval = ipatch_dls2_sample_info_get_property(region->sample_info,
548 property_id, value);
549 else
550 {
551 get_from_sample = TRUE;
552 sample = region->sample ? g_object_ref(region->sample) : NULL;
553 }
554 } /* not sample info, is it a DLS text info property? */
555 else
556 retval = ipatch_dls2_info_get_property(region->info,
557 property_id, value);
558
559 IPATCH_ITEM_RUNLOCK(region);
560
561 if(get_from_sample) /* get sample info from linked sample? */
562 {
563 if(sample)
564 {
565 IPATCH_ITEM_RLOCK(sample);
566 ipatch_dls2_sample_info_get_property(sample->sample_info,
567 property_id, value);
568 IPATCH_ITEM_RUNLOCK(sample);
569 g_object_unref(sample);
570 }
571 else
572 {
573 ipatch_dls2_sample_info_get_property(NULL, property_id, value);
574 }
575 }
576 else if(!retval)
577 {
578 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
579 }
580
581 break;
582 }
583 }
584
585 static void
ipatch_dls2_region_item_copy(IpatchItem * dest,IpatchItem * src,IpatchItemCopyLinkFunc link_func,gpointer user_data)586 ipatch_dls2_region_item_copy(IpatchItem *dest, IpatchItem *src,
587 IpatchItemCopyLinkFunc link_func,
588 gpointer user_data)
589 {
590 IpatchDLS2Region *src_reg, *dest_reg;
591 IpatchDLS2Sample *refsample;
592
593 src_reg = IPATCH_DLS2_REGION(src);
594 dest_reg = IPATCH_DLS2_REGION(dest);
595
596 IPATCH_ITEM_RLOCK(src_reg);
597
598 /* duplicate the flags */
599 ipatch_item_set_flags(dest_reg, ipatch_item_get_flags(src_reg)
600 & IPATCH_DLS2_REGION_FLAG_MASK);
601
602 dest_reg->note_range_low = src_reg->note_range_low;
603 dest_reg->note_range_high = src_reg->note_range_high;
604 dest_reg->velocity_range_low = src_reg->velocity_range_low;
605 dest_reg->velocity_range_high = src_reg->velocity_range_high;
606 dest_reg->key_group = src_reg->key_group;
607 dest_reg->layer_group = src_reg->layer_group;
608 dest_reg->phase_group = src_reg->phase_group;
609 dest_reg->channel = src_reg->channel;
610
611 dest_reg->info = ipatch_dls2_info_duplicate(src_reg->info);
612
613 dest_reg->sample_info = src_reg->sample_info
614 ? ipatch_dls2_sample_info_duplicate(src_reg->sample_info) : NULL;
615
616 /* pass the link to the link handler (if any) */
617 refsample = (IpatchDLS2Sample *)
618 IPATCH_ITEM_COPY_LINK_FUNC(IPATCH_ITEM(dest_reg),
619 IPATCH_ITEM(src_reg->sample),
620 link_func, user_data);
621
622 if(refsample)
623 {
624 ipatch_dls2_region_set_sample(dest_reg, refsample);
625 }
626
627 /* duplicate the connection list */
628 dest_reg->conns = ipatch_dls2_conn_list_duplicate(src_reg->conns);
629
630 IPATCH_ITEM_RUNLOCK(src_reg);
631 }
632
633 static void
ipatch_dls2_region_item_remove_full(IpatchItem * item,gboolean full)634 ipatch_dls2_region_item_remove_full(IpatchItem *item, gboolean full)
635 {
636 if(full)
637 {
638 ipatch_dls2_region_set_sample(IPATCH_DLS2_REGION(item), NULL);
639 }
640
641 if(IPATCH_ITEM_CLASS(ipatch_dls2_region_parent_class)->remove_full)
642 {
643 IPATCH_ITEM_CLASS(ipatch_dls2_region_parent_class)->remove_full(item, full);
644 }
645 }
646
647 /**
648 * ipatch_dls2_region_new:
649 *
650 * Create a new DLS region object.
651 *
652 * Returns: Newly created DLS region with a ref count of 1 which the caller
653 * owns.
654 */
655 IpatchDLS2Region *
ipatch_dls2_region_new(void)656 ipatch_dls2_region_new(void)
657 {
658 return (IPATCH_DLS2_REGION(g_object_new(IPATCH_TYPE_DLS2_REGION, NULL)));
659 }
660
661 /**
662 * ipatch_dls2_region_first: (skip)
663 * @iter: Patch item iterator containing #IpatchDLS2Region items
664 *
665 * Gets the first item in a region iterator. A convenience
666 * wrapper for ipatch_iter_first().
667 *
668 * Returns: The first region in @iter or %NULL if empty.
669 */
670 IpatchDLS2Region *
ipatch_dls2_region_first(IpatchIter * iter)671 ipatch_dls2_region_first(IpatchIter *iter)
672 {
673 GObject *obj;
674 g_return_val_if_fail(iter != NULL, NULL);
675
676 obj = ipatch_iter_first(iter);
677
678 if(obj)
679 {
680 return (IPATCH_DLS2_REGION(obj));
681 }
682 else
683 {
684 return (NULL);
685 }
686 }
687
688 /**
689 * ipatch_dls2_region_next: (skip)
690 * @iter: Patch item iterator containing #IpatchDLS2Region items
691 *
692 * Gets the next item in a region iterator. A convenience wrapper
693 * for ipatch_iter_next().
694 *
695 * Returns: The next region in @iter or %NULL if at the end of
696 * the list.
697 */
698 IpatchDLS2Region *
ipatch_dls2_region_next(IpatchIter * iter)699 ipatch_dls2_region_next(IpatchIter *iter)
700 {
701 GObject *obj;
702 g_return_val_if_fail(iter != NULL, NULL);
703
704 obj = ipatch_iter_next(iter);
705
706 if(obj)
707 {
708 return (IPATCH_DLS2_REGION(obj));
709 }
710 else
711 {
712 return (NULL);
713 }
714 }
715
716 /**
717 * ipatch_dls2_region_get_info:
718 * @region: DLS region to get info from
719 * @fourcc: FOURCC integer id of INFO to get
720 *
721 * Get a DLS region info string by FOURCC integer ID (integer
722 * representation of a 4 character RIFF chunk ID, see
723 * #IpatchRiff).
724 *
725 * Returns: New allocated info string value or %NULL if no info with the
726 * given @fourcc ID. String should be freed when finished with it.
727 */
728 char *
ipatch_dls2_region_get_info(IpatchDLS2Region * region,guint32 fourcc)729 ipatch_dls2_region_get_info(IpatchDLS2Region *region, guint32 fourcc)
730 {
731 char *val;
732
733 g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), NULL);
734
735 IPATCH_ITEM_RLOCK(region);
736 val = ipatch_dls2_info_get(region->info, fourcc);
737 IPATCH_ITEM_RUNLOCK(region);
738
739 return (val);
740 }
741
742 /**
743 * ipatch_dls2_region_set_info:
744 * @region: DLS region to set info of
745 * @fourcc: FOURCC integer ID of INFO to set
746 * @val: (nullable): Value to set info to or %NULL to unset (clear) info.
747 *
748 * Sets an INFO value in a DLS region object.
749 * Emits changed signal.
750 */
751 void
ipatch_dls2_region_set_info(IpatchDLS2Region * region,guint32 fourcc,const char * val)752 ipatch_dls2_region_set_info(IpatchDLS2Region *region, guint32 fourcc,
753 const char *val)
754 {
755 GValue newval = { 0 }, oldval = { 0 };
756
757 g_return_if_fail(IPATCH_IS_DLS2_REGION(region));
758
759 g_value_init(&newval, G_TYPE_STRING);
760 g_value_set_static_string(&newval, val);
761
762 g_value_init(&oldval, G_TYPE_STRING);
763 g_value_take_string(&oldval, ipatch_dls2_region_get_info(region, fourcc));
764
765 IPATCH_ITEM_WLOCK(region);
766 ipatch_dls2_info_set(®ion->info, fourcc, val);
767 IPATCH_ITEM_WUNLOCK(region);
768
769 ipatch_dls2_info_notify((IpatchItem *)region, fourcc, &newval, &oldval);
770
771 g_value_unset(&oldval);
772 g_value_unset(&newval);
773 }
774
775 /**
776 * ipatch_dls2_region_set_sample:
777 * @region: Region to set sample of
778 * @sample: Sample to set region to. Should be NULL or a IpatchDLS2Sample object
779 *
780 * Sets the referenced sample of a region.
781 */
782 void
ipatch_dls2_region_set_sample(IpatchDLS2Region * region,IpatchDLS2Sample * sample)783 ipatch_dls2_region_set_sample(IpatchDLS2Region *region,
784 IpatchDLS2Sample *sample)
785 {
786 g_return_if_fail(IPATCH_IS_DLS2_REGION(region));
787 if(sample != NULL)
788 {
789 g_return_if_fail (IPATCH_IS_DLS2_SAMPLE (sample));
790 }
791
792 ipatch_dls2_region_real_set_sample(region, sample, TRUE);
793 }
794
795 static void
ipatch_dls2_region_real_set_sample(IpatchDLS2Region * region,IpatchDLS2Sample * sample,gboolean sample_notify)796 ipatch_dls2_region_real_set_sample(IpatchDLS2Region *region,
797 IpatchDLS2Sample *sample,
798 gboolean sample_notify)
799 {
800 GValue newval = { 0 }, oldval = { 0 };
801 IpatchDLS2SampleInfo oldinfo, newinfo;
802
803 if(sample_notify)
804 ipatch_item_get_property_fast((IpatchItem *)region, link_item_pspec,
805 &oldval);
806
807 /* get all values of current sample info */
808 ipatch_dls2_region_get_sample_info(region, &oldinfo);
809
810 IPATCH_ITEM_WLOCK(region);
811
812 if(region->sample)
813 {
814 g_object_unref(region->sample);
815 }
816
817 if(sample)
818 {
819 g_object_ref(sample);
820 }
821
822 region->sample = sample;
823 IPATCH_ITEM_WUNLOCK(region);
824
825 if(sample_notify)
826 {
827 g_value_init(&newval, IPATCH_TYPE_DLS2_SAMPLE);
828 g_value_set_object(&newval, sample);
829 ipatch_item_prop_notify((IpatchItem *)region, link_item_pspec,
830 &newval, &oldval);
831 g_value_unset(&newval);
832 g_value_unset(&oldval);
833 }
834
835 /* notify title property change */
836 g_value_init(&newval, G_TYPE_STRING);
837 ipatch_dls2_region_get_title(region, &newval);
838 ipatch_item_prop_notify((IpatchItem *)region, ipatch_item_pspec_title,
839 &newval, NULL);
840 g_value_unset(&newval);
841
842 /* notify for sample info properties */
843 ipatch_dls2_region_get_sample_info(region, &newinfo);
844
845 ipatch_dls2_sample_info_notify_changes((IpatchItem *)region, &newinfo,
846 &oldinfo);
847 }
848
849 static void
ipatch_dls2_region_get_sample_info(IpatchDLS2Region * region,IpatchDLS2SampleInfo * info)850 ipatch_dls2_region_get_sample_info(IpatchDLS2Region *region,
851 IpatchDLS2SampleInfo *info)
852 {
853 IpatchDLS2Sample *sample = NULL;
854 gboolean info_set = TRUE;
855
856 IPATCH_ITEM_RLOCK(region);
857
858 if(ipatch_item_get_flags(region) & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE
859 && region->sample_info)
860 {
861 *info = *region->sample_info;
862 }
863 else if(region->sample)
864 {
865 sample = g_object_ref(region->sample);
866 }
867 else
868 {
869 info_set = FALSE;
870 }
871
872 IPATCH_ITEM_RUNLOCK(region);
873
874 if(sample)
875 {
876 IPATCH_ITEM_RLOCK(sample);
877
878 if(sample->sample_info)
879 {
880 *info = *sample->sample_info;
881 }
882 else
883 {
884 info_set = FALSE;
885 }
886
887 IPATCH_ITEM_RUNLOCK(sample);
888
889 g_object_unref(sample);
890 }
891
892 if(!info_set)
893 {
894 ipatch_dls2_sample_info_init(info);
895 }
896 }
897
898 /**
899 * ipatch_dls2_region_get_sample:
900 * @region: Region to get referenced sample from
901 *
902 * Gets the referenced sample from a region. The returned item's
903 * reference count is incremented and the caller is responsible for
904 * unrefing it with g_object_unref().
905 *
906 * Returns: (transfer full): Region's referenced sample or %NULL if not set yet. Remember to
907 * unreference the item with g_object_unref() when done with it.
908 */
909 IpatchDLS2Sample *
ipatch_dls2_region_get_sample(IpatchDLS2Region * region)910 ipatch_dls2_region_get_sample(IpatchDLS2Region *region)
911 {
912 IpatchDLS2Sample *sample;
913
914 g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), NULL);
915
916 IPATCH_ITEM_RLOCK(region);
917 sample = region->sample;
918
919 if(sample)
920 {
921 g_object_ref(sample);
922 }
923
924 IPATCH_ITEM_RUNLOCK(region);
925
926 return (sample);
927 }
928
929 /**
930 * ipatch_dls2_region_peek_sample: (skip)
931 * @region: Region to get referenced sample from
932 *
933 * Like ipatch_dls2_region_get_sample() but does not add a reference to
934 * the returned item. This function should only be used if a reference
935 * of the returned item is ensured or only the pointer value is of
936 * interest.
937 *
938 * Returns: (transfer none): Region's referenced sample or %NULL if not set yet.
939 * Remember that the item has NOT been referenced.
940 */
941 IpatchDLS2Sample *
ipatch_dls2_region_peek_sample(IpatchDLS2Region * region)942 ipatch_dls2_region_peek_sample(IpatchDLS2Region *region)
943 {
944 IpatchDLS2Sample *sample;
945
946 g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), NULL);
947
948 IPATCH_ITEM_RLOCK(region);
949 sample = region->sample;
950 IPATCH_ITEM_RUNLOCK(region);
951
952 return (sample);
953 }
954
955 /**
956 * ipatch_dls2_region_set_note_range:
957 * @region: Region to set note range of
958 * @low: Low value of range (MIDI note # between 0 and 127)
959 * @high: High value of range (MIDI note # between 0 and 127)
960 *
961 * Set the MIDI note range that a region is active on.
962 */
963 void
ipatch_dls2_region_set_note_range(IpatchDLS2Region * region,int low,int high)964 ipatch_dls2_region_set_note_range(IpatchDLS2Region *region, int low, int high)
965 {
966 g_return_if_fail(IPATCH_IS_DLS2_REGION(region));
967 g_return_if_fail(low >= 0 && low <= 127);
968 g_return_if_fail(high >= 0 && high <= 127);
969
970 if(low > high) /* swap if backwards */
971 {
972 int temp = low;
973 low = high;
974 high = temp;
975 }
976
977 IPATCH_ITEM_WLOCK(region);
978 region->note_range_low = low;
979 region->note_range_high = high;
980 IPATCH_ITEM_WUNLOCK(region);
981 }
982
983 /**
984 * ipatch_dls2_region_set_velocity_range:
985 * @region: Region to set velocity range of
986 * @low: Low value of range (MIDI velocity # between 0 and 127)
987 * @high: High value of range (MIDI velocity # between 0 and 127)
988 *
989 * Set the MIDI velocity range that a region is active on.
990 */
991 void
ipatch_dls2_region_set_velocity_range(IpatchDLS2Region * region,int low,int high)992 ipatch_dls2_region_set_velocity_range(IpatchDLS2Region *region,
993 int low, int high)
994 {
995 g_return_if_fail(IPATCH_IS_DLS2_REGION(region));
996 g_return_if_fail(low >= 0 && low <= 127);
997 g_return_if_fail(high >= 0 && high <= 127);
998
999 if(low > high) /* swap if backwards */
1000 {
1001 int temp = low;
1002 low = high;
1003 high = temp;
1004 }
1005
1006 IPATCH_ITEM_WLOCK(region);
1007 region->velocity_range_low = low;
1008 region->velocity_range_high = high;
1009 IPATCH_ITEM_WUNLOCK(region);
1010 }
1011
1012 /**
1013 * ipatch_dls2_region_in_range:
1014 * @region: Region to check if in range
1015 * @note: MIDI note number or -1 for wildcard
1016 * @velocity: MIDI velocity or -1 for wildcard
1017 *
1018 * Check if a note and velocity falls in a region's ranges
1019 *
1020 * Returns: %TRUE if region is in note and velocity range, %FALSE otherwise
1021 */
1022 gboolean
ipatch_dls2_region_in_range(IpatchDLS2Region * region,int note,int velocity)1023 ipatch_dls2_region_in_range(IpatchDLS2Region *region, int note, int velocity)
1024 {
1025 gboolean in_range;
1026
1027 g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), FALSE);
1028
1029 IPATCH_ITEM_RLOCK(region);
1030
1031 in_range = (note == -1 || (note >= region->note_range_low
1032 && note <= region->note_range_high))
1033 && (velocity == -1 || (velocity >= region->velocity_range_low
1034 && velocity <= region->velocity_range_high));
1035
1036 IPATCH_ITEM_RUNLOCK(region);
1037
1038 return (in_range);
1039 }
1040
1041 /**
1042 * ipatch_dls2_region_set_param:
1043 * @region: Region to set parameter of
1044 * @param: Parameter to set
1045 * @val: Value for parameter
1046 *
1047 * Sets an effect parameter of a DLS2 Region. DLS2 defines a standard set
1048 * of connections (effect parameters). Any non-standard connections can be
1049 * manipulated with the connection related functions.
1050 */
1051 void
ipatch_dls2_region_set_param(IpatchDLS2Region * region,IpatchDLS2Param param,gint32 val)1052 ipatch_dls2_region_set_param(IpatchDLS2Region *region,
1053 IpatchDLS2Param param, gint32 val)
1054 {
1055 g_return_if_fail(IPATCH_IS_DLS2_REGION(region));
1056 g_return_if_fail(param < IPATCH_DLS2_PARAM_COUNT);
1057
1058 /* no need to lock, since write of 32 bit int is atomic */
1059 region->params.values[param] = val;
1060 }
1061
1062 /**
1063 * ipatch_dls2_region_set_param_array:
1064 * @region: Region to set parameter of
1065 * @array: Array of parameter values to copy to region
1066 *
1067 * Sets all effect parameters of a DLS2 Region.
1068 */
1069 void
ipatch_dls2_region_set_param_array(IpatchDLS2Region * region,IpatchDLS2ParamArray * array)1070 ipatch_dls2_region_set_param_array(IpatchDLS2Region *region,
1071 IpatchDLS2ParamArray *array)
1072 {
1073 int i;
1074 g_return_if_fail(IPATCH_IS_DLS2_REGION(region));
1075 g_return_if_fail(array != NULL);
1076
1077 /* Write of each parameter is atomic. */
1078 for(i = 0; i < IPATCH_DLS2_PARAM_COUNT; i++)
1079 {
1080 region->params.values[i] = array->values[i];
1081 }
1082 }
1083
1084 /**
1085 * ipatch_dls2_region_get_conns:
1086 * @region: Region to get connections from
1087 *
1088 * Gets a list of connections from a DLS region. List should be freed with
1089 * ipatch_dls2_conn_list_free() (free_conns set to %TRUE) when finished
1090 * with it.
1091 *
1092 * Returns: (element-type IpatchDLS2Conn) (transfer full): New list of connections
1093 * (#IpatchDLS2Conn) in @region or %NULL if no connections. Remember to free
1094 * it when finished.
1095 */
1096 GSList *
ipatch_dls2_region_get_conns(IpatchDLS2Region * region)1097 ipatch_dls2_region_get_conns(IpatchDLS2Region *region)
1098 {
1099 GSList *newlist;
1100
1101 g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), NULL);
1102
1103 IPATCH_ITEM_RLOCK(region);
1104 newlist = ipatch_dls2_conn_list_duplicate(region->conns);
1105 IPATCH_ITEM_RUNLOCK(region);
1106
1107 return (newlist);
1108 }
1109
1110 /**
1111 * ipatch_dls2_region_set_conn:
1112 * @region: DLS region
1113 * @conn: Connection
1114 *
1115 * Set a DLS connection in a region. See ipatch_dls2_conn_list_set() for
1116 * more details.
1117 */
1118 void
ipatch_dls2_region_set_conn(IpatchDLS2Region * region,const IpatchDLS2Conn * conn)1119 ipatch_dls2_region_set_conn(IpatchDLS2Region *region,
1120 const IpatchDLS2Conn *conn)
1121 {
1122 g_return_if_fail(IPATCH_IS_DLS2_REGION(region));
1123 g_return_if_fail(conn != NULL);
1124
1125 IPATCH_ITEM_WLOCK(region);
1126 ipatch_dls2_conn_list_set(®ion->conns, conn);
1127 IPATCH_ITEM_WUNLOCK(region);
1128 }
1129
1130 /**
1131 * ipatch_dls2_region_unset_conn:
1132 * @region: DLS region
1133 * @conn: Connection
1134 *
1135 * Remove a DLS connection from a region. See ipatch_dls2_conn_list_unset()
1136 * for more details.
1137 */
1138 void
ipatch_dls2_region_unset_conn(IpatchDLS2Region * region,const IpatchDLS2Conn * conn)1139 ipatch_dls2_region_unset_conn(IpatchDLS2Region *region,
1140 const IpatchDLS2Conn *conn)
1141 {
1142 g_return_if_fail(IPATCH_IS_DLS2_REGION(region));
1143 g_return_if_fail(conn != NULL);
1144
1145 IPATCH_ITEM_WLOCK(region);
1146 ipatch_dls2_conn_list_unset(®ion->conns, conn);
1147 IPATCH_ITEM_WUNLOCK(region);
1148 }
1149
1150 /**
1151 * ipatch_dls2_region_unset_all_conns:
1152 * @region: DLS region
1153 *
1154 * Remove all connections in a region.
1155 */
1156 void
ipatch_dls2_region_unset_all_conns(IpatchDLS2Region * region)1157 ipatch_dls2_region_unset_all_conns(IpatchDLS2Region *region)
1158 {
1159 g_return_if_fail(IPATCH_IS_DLS2_REGION(region));
1160
1161 IPATCH_ITEM_WLOCK(region);
1162 ipatch_dls2_conn_list_free(region->conns, TRUE);
1163 region->conns = NULL;
1164 IPATCH_ITEM_WUNLOCK(region);
1165 }
1166
1167 /**
1168 * ipatch_dls2_region_conn_count:
1169 * @region: Region to count connections in
1170 *
1171 * Count number of connections in a region
1172 *
1173 * Returns: Count of connections
1174 */
1175 guint
ipatch_dls2_region_conn_count(IpatchDLS2Region * region)1176 ipatch_dls2_region_conn_count(IpatchDLS2Region *region)
1177 {
1178 guint i;
1179
1180 IPATCH_ITEM_RLOCK(region);
1181 i = g_slist_length(region->conns);
1182 IPATCH_ITEM_RUNLOCK(region);
1183
1184 return (i);
1185 }
1186
1187 /**
1188 * ipatch_dls2_region_channel_map_stereo:
1189 * @chan: Channel steering enum
1190 *
1191 * Map a DLS2 channel steering enumeration (surround sound capable) to stereo
1192 * steering.
1193 *
1194 * Returns: -1 = left, 0 = center, 1 = right
1195 */
1196 int
ipatch_dls2_region_channel_map_stereo(IpatchDLS2RegionChannelType chan)1197 ipatch_dls2_region_channel_map_stereo(IpatchDLS2RegionChannelType chan)
1198 {
1199 switch(chan)
1200 {
1201 case IPATCH_DLS2_REGION_CHANNEL_LEFT:
1202 case IPATCH_DLS2_REGION_CHANNEL_SURROUND_LEFT:
1203 case IPATCH_DLS2_REGION_CHANNEL_LEFT_OF_CENTER:
1204 case IPATCH_DLS2_REGION_CHANNEL_SIDE_LEFT:
1205 case IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_LEFT:
1206 case IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_LEFT:
1207 return (-1);
1208
1209 case IPATCH_DLS2_REGION_CHANNEL_RIGHT:
1210 case IPATCH_DLS2_REGION_CHANNEL_SURROUND_RIGHT:
1211 case IPATCH_DLS2_REGION_CHANNEL_RIGHT_OF_CENTER:
1212 case IPATCH_DLS2_REGION_CHANNEL_SIDE_RIGHT:
1213 case IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_RIGHT:
1214 case IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_RIGHT:
1215 return (1);
1216
1217 case IPATCH_DLS2_REGION_CHANNEL_CENTER:
1218 case IPATCH_DLS2_REGION_CHANNEL_LOW_FREQ:
1219 case IPATCH_DLS2_REGION_CHANNEL_SURROUND_CENTER:
1220 case IPATCH_DLS2_REGION_CHANNEL_TOP:
1221 case IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_CENTER:
1222 case IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_CENTER:
1223 return (0);
1224
1225 default:
1226 return (0);
1227 }
1228 }
1229