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(&region->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(&region->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(&region->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(&region->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(&region->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