1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2020 Joël Krähemann
3  *
4  * This file is part of GSequencer.
5  *
6  * GSequencer is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSequencer is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSequencer.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <ags/audio/file/ags_sfz_file.h>
21 
22 #include <ags/audio/ags_diatonic_scale.h>
23 
24 #include <ags/audio/file/ags_sound_container.h>
25 #include <ags/audio/file/ags_sound_resource.h>
26 #include <ags/audio/file/ags_sfz_group.h>
27 #include <ags/audio/file/ags_sfz_region.h>
28 #include <ags/audio/file/ags_sfz_sample.h>
29 
30 #include <sys/stat.h>
31 #include <unistd.h>
32 
33 #include <string.h>
34 #include <strings.h>
35 
36 #include <ags/i18n.h>
37 
38 void ags_sfz_file_class_init(AgsSFZFileClass *sfz_file);
39 void ags_sfz_file_connectable_interface_init(AgsConnectableInterface *connectable);
40 void ags_sfz_file_sound_container_interface_init(AgsSoundContainerInterface *sound_container);
41 void ags_sfz_file_init(AgsSFZFile *sfz_file);
42 void ags_sfz_file_set_property(GObject *gobject,
43 			       guint prop_id,
44 			       const GValue *value,
45 			       GParamSpec *param_spec);
46 void ags_sfz_file_get_property(GObject *gobject,
47 			       guint prop_id,
48 			       GValue *value,
49 			       GParamSpec *param_spec);
50 void ags_sfz_file_dispose(GObject *gobject);
51 void ags_sfz_file_finalize(GObject *gobject);
52 
53 AgsUUID* ags_sfz_file_get_uuid(AgsConnectable *connectable);
54 gboolean ags_sfz_file_has_resource(AgsConnectable *connectable);
55 gboolean ags_sfz_file_is_ready(AgsConnectable *connectable);
56 void ags_sfz_file_add_to_registry(AgsConnectable *connectable);
57 void ags_sfz_file_remove_from_registry(AgsConnectable *connectable);
58 xmlNode* ags_sfz_file_list_resource(AgsConnectable *connectable);
59 xmlNode* ags_sfz_file_xml_compose(AgsConnectable *connectable);
60 void ags_sfz_file_xml_parse(AgsConnectable *connectable,
61 			    xmlNode *node);
62 gboolean ags_sfz_file_is_connected(AgsConnectable *connectable);
63 void ags_sfz_file_connect(AgsConnectable *connectable);
64 void ags_sfz_file_disconnect(AgsConnectable *connectable);
65 
66 gboolean ags_sfz_file_open(AgsSoundContainer *sound_container, gchar *filename);
67 guint ags_sfz_file_get_level_count(AgsSoundContainer *sound_container);
68 guint ags_sfz_file_get_nesting_level(AgsSoundContainer *sound_container);
69 gchar* ags_sfz_file_get_level_id(AgsSoundContainer *sound_container);
70 guint ags_sfz_file_get_level_index(AgsSoundContainer *sound_container);
71 guint ags_sfz_file_level_up(AgsSoundContainer *sound_container,
72 			    guint level_count);
73 guint ags_sfz_file_select_level_by_id(AgsSoundContainer *sound_container,
74 				      gchar *level_id);
75 guint ags_sfz_file_select_level_by_index(AgsSoundContainer *sound_container,
76 					 guint level_index);
77 gchar** ags_sfz_file_get_sublevel_name(AgsSoundContainer *sound_container);
78 GList* ags_sfz_file_get_resource_all(AgsSoundContainer *sound_container);
79 GList* ags_sfz_file_get_resource_by_name(AgsSoundContainer *sound_container,
80 					 gchar *resource_name);
81 GList* ags_sfz_file_get_resource_by_index(AgsSoundContainer *sound_container,
82 					  guint resource_index);
83 GList* ags_sfz_file_get_resource_current(AgsSoundContainer *sound_container);
84 void ags_sfz_file_close(AgsSoundContainer *sound_container);
85 
86 gchar* ags_sfz_file_parse_skip_comments_and_blanks(gchar *buffer, gsize buffer_length,
87 						   gchar **iter);
88 
89 /**
90  * SECTION:ags_sfz_file
91  * @short_description: SFZ file
92  * @title: AgsSFZFile
93  * @section_id:
94  * @include: ags/audio/file/ags_sfz_file.h
95  *
96  * #AgsSFZFile is the base object to ineract with SFZ files.
97  */
98 
99 enum{
100   PROP_0,
101   PROP_SOUNDCARD,
102   PROP_FILENAME,
103   PROP_MODE,
104   PROP_GROUP,
105   PROP_REGION,
106   PROP_SAMPLE,
107 };
108 
109 static gpointer ags_sfz_file_parent_class = NULL;
110 
111 static GMutex regex_mutex;
112 
113 GType
ags_sfz_file_get_type()114 ags_sfz_file_get_type()
115 {
116   static volatile gsize g_define_type_id__volatile = 0;
117 
118   if(g_once_init_enter (&g_define_type_id__volatile)){
119     GType ags_type_sfz_file = 0;
120 
121     static const GTypeInfo ags_sfz_file_info = {
122       sizeof (AgsSFZFileClass),
123       NULL, /* base_init */
124       NULL, /* base_finalize */
125       (GClassInitFunc) ags_sfz_file_class_init,
126       NULL, /* class_finalize */
127       NULL, /* class_data */
128       sizeof (AgsSFZFile),
129       0,    /* n_preallocs */
130       (GInstanceInitFunc) ags_sfz_file_init,
131     };
132 
133     static const GInterfaceInfo ags_connectable_interface_info = {
134       (GInterfaceInitFunc) ags_sfz_file_connectable_interface_init,
135       NULL, /* interface_finalize */
136       NULL, /* interface_data */
137     };
138 
139     static const GInterfaceInfo ags_sound_container_interface_info = {
140       (GInterfaceInitFunc) ags_sfz_file_sound_container_interface_init,
141       NULL, /* interface_finalize */
142       NULL, /* interface_data */
143     };
144 
145     ags_type_sfz_file = g_type_register_static(G_TYPE_OBJECT,
146 					       "AgsSFZFile",
147 					       &ags_sfz_file_info,
148 					       0);
149 
150     g_type_add_interface_static(ags_type_sfz_file,
151 				AGS_TYPE_CONNECTABLE,
152 				&ags_connectable_interface_info);
153 
154     g_type_add_interface_static(ags_type_sfz_file,
155 				AGS_TYPE_SOUND_CONTAINER,
156 				&ags_sound_container_interface_info);
157 
158     g_once_init_leave(&g_define_type_id__volatile, ags_type_sfz_file);
159   }
160 
161   return g_define_type_id__volatile;
162 }
163 
164 void
ags_sfz_file_class_init(AgsSFZFileClass * sfz_file)165 ags_sfz_file_class_init(AgsSFZFileClass *sfz_file)
166 {
167   GObjectClass *gobject;
168   GParamSpec *param_spec;
169 
170   ags_sfz_file_parent_class = g_type_class_peek_parent(sfz_file);
171 
172   /* GObjectClass */
173   gobject = (GObjectClass *) sfz_file;
174 
175   gobject->set_property = ags_sfz_file_set_property;
176   gobject->get_property = ags_sfz_file_get_property;
177 
178   gobject->dispose = ags_sfz_file_dispose;
179   gobject->finalize = ags_sfz_file_finalize;
180 
181   /* properties */
182   /**
183    * AgsSFZFile:soundcard:
184    *
185    * The assigned soundcard.
186    *
187    * Since: 3.0.0
188    */
189   param_spec = g_param_spec_object("soundcard",
190 				   i18n_pspec("soundcard of sfz_file"),
191 				   i18n_pspec("The soundcard what sfz_file has it's presets"),
192 				   G_TYPE_OBJECT,
193 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
194   g_object_class_install_property(gobject,
195 				  PROP_SOUNDCARD,
196 				  param_spec);
197 
198   /**
199    * AgsSFZFile:filename:
200    *
201    * The assigned filename.
202    *
203    * Since: 3.0.0
204    */
205   param_spec = g_param_spec_string("filename",
206 				   i18n_pspec("the filename"),
207 				   i18n_pspec("The filename to open"),
208 				   NULL,
209 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
210   g_object_class_install_property(gobject,
211 				  PROP_FILENAME,
212 				  param_spec);
213 
214   /**
215    * AgsSFZFile:mode:
216    *
217    * The assigned mode.
218    *
219    * Since: 3.0.0
220    */
221   param_spec = g_param_spec_string("mode",
222 				   i18n_pspec("the mode"),
223 				   i18n_pspec("The mode to open the file"),
224 				   NULL,
225 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
226   g_object_class_install_property(gobject,
227 				  PROP_MODE,
228 				  param_spec);
229 
230   /**
231    * AgsSFZFile:group: (type GList(AgsSFZGroup)) (transfer full)
232    *
233    * The containing groups.
234    *
235    * Since: 3.0.0
236    */
237   param_spec = g_param_spec_pointer("group",
238 				    i18n_pspec("containing group"),
239 				    i18n_pspec("The containing groups"),
240 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
241   g_object_class_install_property(gobject,
242 				  PROP_GROUP,
243 				  param_spec);
244 
245   /**
246    * AgsSFZFile:region: (type GList(AgsSFZRegion)) (transfer full)
247    *
248    * The containing regions.
249    *
250    * Since: 3.0.0
251    */
252   param_spec = g_param_spec_pointer("region",
253 				    i18n_pspec("containing region"),
254 				    i18n_pspec("The containing regions"),
255 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
256   g_object_class_install_property(gobject,
257 				  PROP_REGION,
258 				  param_spec);
259 
260   /**
261    * AgsSFZFile:sample: (type GList(AgsSFZSample)) (transfer full)
262    *
263    * The containing samples.
264    *
265    * Since: 3.0.0
266    */
267   param_spec = g_param_spec_pointer("sample",
268 				    i18n_pspec("containing sample"),
269 				    i18n_pspec("The containing samples"),
270 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
271   g_object_class_install_property(gobject,
272 				  PROP_SAMPLE,
273 				  param_spec);
274 }
275 
276 void
ags_sfz_file_connectable_interface_init(AgsConnectableInterface * connectable)277 ags_sfz_file_connectable_interface_init(AgsConnectableInterface *connectable)
278 {
279   connectable->get_uuid = ags_sfz_file_get_uuid;
280   connectable->has_resource = ags_sfz_file_has_resource;
281   connectable->is_ready = ags_sfz_file_is_ready;
282 
283   connectable->add_to_registry = ags_sfz_file_add_to_registry;
284   connectable->remove_from_registry = ags_sfz_file_remove_from_registry;
285 
286   connectable->list_resource = ags_sfz_file_list_resource;
287   connectable->xml_compose = ags_sfz_file_xml_compose;
288   connectable->xml_parse = ags_sfz_file_xml_parse;
289 
290   connectable->is_connected = ags_sfz_file_is_connected;
291 
292   connectable->connect = ags_sfz_file_connect;
293   connectable->disconnect = ags_sfz_file_disconnect;
294 
295   connectable->connect_connection = NULL;
296   connectable->disconnect_connection = NULL;
297 }
298 
299 void
ags_sfz_file_sound_container_interface_init(AgsSoundContainerInterface * sound_container)300 ags_sfz_file_sound_container_interface_init(AgsSoundContainerInterface *sound_container)
301 {
302   sound_container->open = ags_sfz_file_open;
303 
304   sound_container->get_level_count = ags_sfz_file_get_level_count;
305   sound_container->get_nesting_level = ags_sfz_file_get_nesting_level;
306 
307   sound_container->get_level_id = ags_sfz_file_get_level_id;
308   sound_container->get_level_index = ags_sfz_file_get_level_index;
309 
310   sound_container->get_sublevel_name = ags_sfz_file_get_sublevel_name;
311 
312   sound_container->level_up = ags_sfz_file_level_up;
313   sound_container->select_level_by_id = ags_sfz_file_select_level_by_id;
314   sound_container->select_level_by_index = ags_sfz_file_select_level_by_index;
315 
316   sound_container->get_resource_all = ags_sfz_file_get_resource_all;
317   sound_container->get_resource_by_name = ags_sfz_file_get_resource_by_name;
318   sound_container->get_resource_by_index = ags_sfz_file_get_resource_by_index;
319   sound_container->get_resource_current = ags_sfz_file_get_resource_current;
320 
321   sound_container->close = ags_sfz_file_close;
322 }
323 
324 void
ags_sfz_file_init(AgsSFZFile * sfz_file)325 ags_sfz_file_init(AgsSFZFile *sfz_file)
326 {
327   guint i;
328 
329   sfz_file->flags = 0;
330 
331   /* add audio file mutex */
332   g_rec_mutex_init(&(sfz_file->obj_mutex));
333 
334   /* uuid */
335   sfz_file->uuid = ags_uuid_alloc();
336   ags_uuid_generate(sfz_file->uuid);
337 
338   sfz_file->soundcard = NULL;
339 
340   sfz_file->filename = NULL;
341   sfz_file->mode = AGS_SFZ_FILE_READ;
342 
343   sfz_file->file = NULL;
344 
345   sfz_file->nesting_level = 0;
346 
347   sfz_file->level_id = NULL;
348   sfz_file->level_index = 0;
349 
350   sfz_file->reader = NULL;
351   sfz_file->writer = NULL;
352 
353   sfz_file->group = NULL;
354   sfz_file->region = NULL;
355   sfz_file->sample = NULL;
356 
357   /* selected */
358   sfz_file->index_selected = (guint *) malloc(2 * sizeof(guint));
359   memset(sfz_file->index_selected, 0, 2 * sizeof(guint));
360 
361   sfz_file->name_selected = (gchar **) malloc(3 * sizeof(gchar *));
362 
363   for(i = 0; i < 3; i++){
364     sfz_file->name_selected[i] = NULL;
365   }
366 
367   sfz_file->current_sample = NULL;
368 
369   sfz_file->audio_signal= NULL;
370 }
371 
372 void
ags_sfz_file_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)373 ags_sfz_file_set_property(GObject *gobject,
374 			  guint prop_id,
375 			  const GValue *value,
376 			  GParamSpec *param_spec)
377 {
378   AgsSFZFile *sfz_file;
379 
380   GRecMutex *sfz_file_mutex;
381 
382   sfz_file = AGS_SFZ_FILE(gobject);
383 
384   /* get sfz file mutex */
385   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
386 
387   switch(prop_id){
388   case PROP_SOUNDCARD:
389   {
390     GObject *soundcard;
391 
392     soundcard = (GObject *) g_value_get_object(value);
393 
394     g_rec_mutex_lock(sfz_file_mutex);
395 
396     if(soundcard == ((GObject *) sfz_file->soundcard)){
397       g_rec_mutex_unlock(sfz_file_mutex);
398 
399       return;
400     }
401 
402     if(sfz_file->soundcard != NULL){
403       g_object_unref(sfz_file->soundcard);
404     }
405 
406     if(soundcard != NULL){
407       g_object_ref(G_OBJECT(soundcard));
408     }
409 
410     sfz_file->soundcard = (GObject *) soundcard;
411 
412     g_rec_mutex_unlock(sfz_file_mutex);
413   }
414   break;
415   case PROP_FILENAME:
416   {
417     gchar *filename;
418 
419     filename = (gchar *) g_value_get_string(value);
420 
421     ags_sound_container_open(AGS_SOUND_CONTAINER(sfz_file), filename);
422   }
423   break;
424   case PROP_MODE:
425   {
426     gchar *mode;
427 
428     mode = (gchar *) g_value_get_string(value);
429 
430     g_rec_mutex_lock(sfz_file_mutex);
431 
432     sfz_file->mode = mode;
433 
434     g_rec_mutex_lock(sfz_file_mutex);
435   }
436   break;
437   case PROP_GROUP:
438   {
439     GObject *group;
440 
441     group = g_value_get_pointer(value);
442 
443     g_rec_mutex_lock(sfz_file_mutex);
444 
445     if(group == NULL ||
446        g_list_find(sfz_file->group, group) != NULL){
447       g_rec_mutex_unlock(sfz_file_mutex);
448 
449       return;
450     }
451 
452     g_object_ref(group);
453     sfz_file->group = g_list_prepend(sfz_file->group,
454 				     group);
455 
456     g_rec_mutex_unlock(sfz_file_mutex);
457   }
458   break;
459   case PROP_REGION:
460   {
461     GObject *region;
462 
463     region = g_value_get_pointer(value);
464 
465     g_rec_mutex_lock(sfz_file_mutex);
466 
467     if(region == NULL ||
468        g_list_find(sfz_file->region, region) != NULL){
469       g_rec_mutex_unlock(sfz_file_mutex);
470 
471       return;
472     }
473 
474     g_object_ref(region);
475     sfz_file->region = g_list_prepend(sfz_file->region,
476 				      region);
477 
478     g_rec_mutex_unlock(sfz_file_mutex);
479   }
480   break;
481   case PROP_SAMPLE:
482   {
483     GObject *sample;
484 
485     sample = g_value_get_pointer(value);
486 
487     g_rec_mutex_lock(sfz_file_mutex);
488 
489     if(sample == NULL ||
490        g_list_find(sfz_file->sample, sample) != NULL){
491       g_rec_mutex_unlock(sfz_file_mutex);
492 
493       return;
494     }
495 
496     g_object_ref(sample);
497     sfz_file->sample = g_list_prepend(sfz_file->sample,
498 				      sample);
499 
500     g_rec_mutex_unlock(sfz_file_mutex);
501   }
502   break;
503   default:
504     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
505     break;
506   }
507 }
508 
509 void
ags_sfz_file_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)510 ags_sfz_file_get_property(GObject *gobject,
511 			  guint prop_id,
512 			  GValue *value,
513 			  GParamSpec *param_spec)
514 {
515   AgsSFZFile *sfz_file;
516 
517   GRecMutex *sfz_file_mutex;
518 
519   sfz_file = AGS_SFZ_FILE(gobject);
520 
521   /* get sfz file mutex */
522   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
523 
524   switch(prop_id){
525   case PROP_SOUNDCARD:
526   {
527     g_rec_mutex_lock(sfz_file_mutex);
528 
529     g_value_set_object(value, sfz_file->soundcard);
530 
531     g_rec_mutex_unlock(sfz_file_mutex);
532   }
533   break;
534   case PROP_FILENAME:
535   {
536     g_rec_mutex_lock(sfz_file_mutex);
537 
538     g_value_set_string(value, sfz_file->filename);
539 
540     g_rec_mutex_unlock(sfz_file_mutex);
541   }
542   break;
543   case PROP_MODE:
544   {
545     g_rec_mutex_lock(sfz_file_mutex);
546 
547     g_value_set_string(value, sfz_file->mode);
548 
549     g_rec_mutex_unlock(sfz_file_mutex);
550   }
551   break;
552   case PROP_GROUP:
553   {
554     g_rec_mutex_lock(sfz_file_mutex);
555 
556     g_value_set_pointer(value, g_list_copy_deep(sfz_file->group,
557 						(GCopyFunc) g_object_ref,
558 						NULL));
559 
560     g_rec_mutex_unlock(sfz_file_mutex);
561   }
562   break;
563   case PROP_REGION:
564   {
565     g_rec_mutex_lock(sfz_file_mutex);
566 
567     g_value_set_pointer(value, g_list_copy_deep(sfz_file->region,
568 						(GCopyFunc) g_object_ref,
569 						NULL));
570 
571     g_rec_mutex_unlock(sfz_file_mutex);
572   }
573   break;
574   case PROP_SAMPLE:
575   {
576     g_rec_mutex_lock(sfz_file_mutex);
577 
578     g_value_set_pointer(value, g_list_copy_deep(sfz_file->sample,
579 						(GCopyFunc) g_object_ref,
580 						NULL));
581 
582     g_rec_mutex_unlock(sfz_file_mutex);
583   }
584   break;
585   default:
586     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
587     break;
588   }
589 }
590 
591 void
ags_sfz_file_dispose(GObject * gobject)592 ags_sfz_file_dispose(GObject *gobject)
593 {
594   AgsSFZFile *sfz_file;
595 
596   sfz_file = AGS_SFZ_FILE(gobject);
597 
598   if(sfz_file->group != NULL){
599     g_list_free_full(sfz_file->group,
600 		     g_object_unref);
601 
602     sfz_file->group = NULL;
603   }
604 
605   if(sfz_file->region != NULL){
606     g_list_free_full(sfz_file->region,
607 		     g_object_unref);
608 
609     sfz_file->region = NULL;
610   }
611 
612   if(sfz_file->sample != NULL){
613     g_list_free_full(sfz_file->sample,
614 		     g_object_unref);
615 
616     sfz_file->sample = NULL;
617   }
618 
619   /* call parent */
620   G_OBJECT_CLASS(ags_sfz_file_parent_class)->dispose(gobject);
621 }
622 
623 void
ags_sfz_file_finalize(GObject * gobject)624 ags_sfz_file_finalize(GObject *gobject)
625 {
626   AgsSFZFile *sfz_file;
627 
628   sfz_file = AGS_SFZ_FILE(gobject);
629 
630   if(sfz_file->soundcard != NULL){
631     g_object_unref(sfz_file->soundcard);
632   }
633 
634   g_free(sfz_file->filename);
635   g_free(sfz_file->mode);
636 
637   if(sfz_file->reader != NULL){
638     g_object_unref(sfz_file->reader);
639   }
640 
641   if(sfz_file->group != NULL){
642     g_list_free_full(sfz_file->group,
643 		     g_object_unref);
644   }
645 
646   if(sfz_file->region != NULL){
647     g_list_free_full(sfz_file->region,
648 		     g_object_unref);
649   }
650 
651   if(sfz_file->sample != NULL){
652     g_list_free_full(sfz_file->sample,
653 		     g_object_unref);
654   }
655 
656   g_list_free_full(sfz_file->audio_signal,
657 		   g_object_unref);
658 
659   /* call parent */
660   G_OBJECT_CLASS(ags_sfz_file_parent_class)->finalize(gobject);
661 }
662 
663 AgsUUID*
ags_sfz_file_get_uuid(AgsConnectable * connectable)664 ags_sfz_file_get_uuid(AgsConnectable *connectable)
665 {
666   AgsSFZFile *sfz_file;
667 
668   AgsUUID *ptr;
669 
670   GRecMutex *sfz_file_mutex;
671 
672   sfz_file = AGS_SFZ_FILE(connectable);
673 
674   /* get audio file mutex */
675   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
676 
677   /* get UUID */
678   g_rec_mutex_lock(sfz_file_mutex);
679 
680   ptr = sfz_file->uuid;
681 
682   g_rec_mutex_unlock(sfz_file_mutex);
683 
684   return(ptr);
685 }
686 
687 gboolean
ags_sfz_file_has_resource(AgsConnectable * connectable)688 ags_sfz_file_has_resource(AgsConnectable *connectable)
689 {
690   return(TRUE);
691 }
692 
693 gboolean
ags_sfz_file_is_ready(AgsConnectable * connectable)694 ags_sfz_file_is_ready(AgsConnectable *connectable)
695 {
696   AgsSFZFile *sfz_file;
697 
698   gboolean is_ready;
699 
700   sfz_file = AGS_SFZ_FILE(connectable);
701 
702   /* check is ready */
703   is_ready = ags_sfz_file_test_flags(sfz_file, AGS_SFZ_FILE_ADDED_TO_REGISTRY);
704 
705   return(is_ready);
706 }
707 
708 void
ags_sfz_file_add_to_registry(AgsConnectable * connectable)709 ags_sfz_file_add_to_registry(AgsConnectable *connectable)
710 {
711   AgsSFZFile *sfz_file;
712 
713   AgsRegistry *registry;
714   AgsRegistryEntry *entry;
715 
716   AgsApplicationContext *application_context;
717 
718   if(ags_connectable_is_ready(connectable)){
719     return;
720   }
721 
722   sfz_file = AGS_SFZ_FILE(connectable);
723 
724   ags_sfz_file_set_flags(sfz_file, AGS_SFZ_FILE_ADDED_TO_REGISTRY);
725 
726   application_context = ags_application_context_get_instance();
727 
728   registry = (AgsRegistry *) ags_service_provider_get_registry(AGS_SERVICE_PROVIDER(application_context));
729 
730   if(registry != NULL){
731     entry = ags_registry_entry_alloc(registry);
732     g_value_set_object(entry->entry,
733 		       (gpointer) sfz_file);
734     ags_registry_add_entry(registry,
735 			   entry);
736   }
737 }
738 
739 void
ags_sfz_file_remove_from_registry(AgsConnectable * connectable)740 ags_sfz_file_remove_from_registry(AgsConnectable *connectable)
741 {
742   if(!ags_connectable_is_ready(connectable)){
743     return;
744   }
745 
746   //TODO:JK: implement me
747 }
748 
749 xmlNode*
ags_sfz_file_list_resource(AgsConnectable * connectable)750 ags_sfz_file_list_resource(AgsConnectable *connectable)
751 {
752   xmlNode *node;
753 
754   node = NULL;
755 
756   //TODO:JK: implement me
757 
758   return(node);
759 }
760 
761 xmlNode*
ags_sfz_file_xml_compose(AgsConnectable * connectable)762 ags_sfz_file_xml_compose(AgsConnectable *connectable)
763 {
764   xmlNode *node;
765 
766   node = NULL;
767 
768   //TODO:JK: implement me
769 
770   return(node);
771 }
772 
773 void
ags_sfz_file_xml_parse(AgsConnectable * connectable,xmlNode * node)774 ags_sfz_file_xml_parse(AgsConnectable *connectable,
775 		       xmlNode *node)
776 {
777   //TODO:JK: implement me
778 }
779 
780 gboolean
ags_sfz_file_is_connected(AgsConnectable * connectable)781 ags_sfz_file_is_connected(AgsConnectable *connectable)
782 {
783   AgsSFZFile *sfz_file;
784 
785   gboolean is_connected;
786 
787   sfz_file = AGS_SFZ_FILE(connectable);
788 
789   /* check is connected */
790   is_connected = ags_sfz_file_test_flags(sfz_file, AGS_SFZ_FILE_CONNECTED);
791 
792   return(is_connected);
793 }
794 
795 void
ags_sfz_file_connect(AgsConnectable * connectable)796 ags_sfz_file_connect(AgsConnectable *connectable)
797 {
798   AgsSFZFile *sfz_file;
799 
800   if(ags_connectable_is_connected(connectable)){
801     return;
802   }
803 
804   sfz_file = AGS_SFZ_FILE(connectable);
805 
806   ags_sfz_file_set_flags(sfz_file, AGS_SFZ_FILE_CONNECTED);
807 }
808 
809 void
ags_sfz_file_disconnect(AgsConnectable * connectable)810 ags_sfz_file_disconnect(AgsConnectable *connectable)
811 {
812   AgsSFZFile *sfz_file;
813 
814   if(!ags_connectable_is_connected(connectable)){
815     return;
816   }
817 
818   sfz_file = AGS_SFZ_FILE(connectable);
819 
820   ags_sfz_file_unset_flags(sfz_file, AGS_SFZ_FILE_CONNECTED);
821 }
822 
823 gboolean
ags_sfz_file_open(AgsSoundContainer * sound_container,gchar * filename)824 ags_sfz_file_open(AgsSoundContainer *sound_container, gchar *filename)
825 {
826   AgsSFZFile *sfz_file;
827 
828   FILE *file;
829 
830   gchar *old_filename;
831 
832   gboolean retval;
833 
834   GRecMutex *sfz_file_mutex;
835 
836   sfz_file = AGS_SFZ_FILE(sound_container);
837 
838   /* get sfz_file mutex */
839   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
840 
841   /* get some fields */
842   g_rec_mutex_lock(sfz_file_mutex);
843 
844   old_filename = sfz_file->filename;
845 
846   g_rec_mutex_unlock(sfz_file_mutex);
847 
848   /* close current */
849   if(old_filename != NULL){
850     ags_sound_container_close(sound_container);
851 
852     g_free(old_filename);
853   }
854 
855   /* check suffix */
856   g_rec_mutex_lock(sfz_file_mutex);
857 
858   sfz_file->filename = g_strdup(filename);
859 
860   g_rec_mutex_unlock(sfz_file_mutex);
861 
862   if(!ags_sfz_file_check_suffix(filename)){
863     g_message("unsupported suffix");
864 
865     return(FALSE);
866   }
867 
868   /* open file */
869   file = fopen(filename,
870 	       "r");
871 
872   g_rec_mutex_lock(sfz_file_mutex);
873 
874   sfz_file->file = file;
875 
876   g_rec_mutex_unlock(sfz_file_mutex);
877 
878   if(file == NULL){
879     g_message("failed to open file");
880 
881     return(FALSE);
882   }
883 
884   /* load samples */
885   retval = TRUE;
886 
887   ags_sfz_file_parse(sfz_file);
888 
889   return(retval);
890 }
891 
892 guint
ags_sfz_file_get_level_count(AgsSoundContainer * sound_container)893 ags_sfz_file_get_level_count(AgsSoundContainer *sound_container)
894 {
895   AgsSFZFile *sfz_file;
896 
897   sfz_file = AGS_SFZ_FILE(sound_container);
898 
899   return(3);
900 }
901 
902 guint
ags_sfz_file_get_nesting_level(AgsSoundContainer * sound_container)903 ags_sfz_file_get_nesting_level(AgsSoundContainer *sound_container)
904 {
905   AgsSFZFile *sfz_file;
906 
907   guint nesting_level;
908 
909   GRecMutex *sfz_file_mutex;
910 
911   sfz_file = AGS_SFZ_FILE(sound_container);
912 
913   /* get sfz_file mutex */
914   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
915 
916   /* get nesting level */
917   g_rec_mutex_lock(sfz_file_mutex);
918 
919   nesting_level = sfz_file->nesting_level;
920 
921   g_rec_mutex_unlock(sfz_file_mutex);
922 
923   return(nesting_level);
924 }
925 
926 gchar*
ags_sfz_file_get_level_id(AgsSoundContainer * sound_container)927 ags_sfz_file_get_level_id(AgsSoundContainer *sound_container)
928 {
929   AgsSFZFile *sfz_file;
930 
931   gchar *level_id;
932 
933   GRecMutex *sfz_file_mutex;
934 
935   sfz_file = AGS_SFZ_FILE(sound_container);
936 
937   /* get sfz_file mutex */
938   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
939 
940   /* get level id */
941   g_rec_mutex_lock(sfz_file_mutex);
942 
943   level_id = sfz_file->level_id;
944 
945   g_rec_mutex_unlock(sfz_file_mutex);
946 
947   return(level_id);
948 }
949 
950 guint
ags_sfz_file_get_level_index(AgsSoundContainer * sound_container)951 ags_sfz_file_get_level_index(AgsSoundContainer *sound_container)
952 {
953   AgsSFZFile *sfz_file;
954 
955   guint level_index;
956 
957   GRecMutex *sfz_file_mutex;
958 
959   sfz_file = AGS_SFZ_FILE(sound_container);
960 
961   /* get sfz_file mutex */
962   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
963 
964   /* get nesting level */
965   g_rec_mutex_lock(sfz_file_mutex);
966 
967   level_index = sfz_file->level_index;
968 
969   g_rec_mutex_unlock(sfz_file_mutex);
970 
971   return(level_index);
972 }
973 
974 gchar**
ags_sfz_file_get_sublevel_name(AgsSoundContainer * sound_container)975 ags_sfz_file_get_sublevel_name(AgsSoundContainer *sound_container)
976 {
977   AgsSFZFile *sfz_file;
978 
979   guint sublevel;
980 
981   GRecMutex *sfz_file_mutex;
982 
983   sfz_file = AGS_SFZ_FILE(sound_container);
984 
985   /* get sfz_file mutex */
986   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
987 
988   /* sublevel */
989   sublevel = ags_sound_container_get_nesting_level(AGS_SOUND_CONTAINER(sfz_file));
990 
991   switch(sublevel){
992   case AGS_SFZ_LEVEL_FILENAME:
993   {
994     gchar **sublevel_name;
995 
996     sublevel_name = (gchar **) malloc(2 * sizeof(gchar*));
997 
998     g_rec_mutex_lock(sfz_file_mutex);
999 
1000     sublevel_name[0] = g_strdup(sfz_file->filename);
1001 
1002     g_rec_mutex_unlock(sfz_file_mutex);
1003 
1004     sublevel_name[1] = NULL;
1005 
1006     return(sublevel_name);
1007   }
1008   case AGS_SFZ_LEVEL_SAMPLE:
1009   {
1010     GList *start_list, *list;
1011 
1012     gchar **sublevel_name;
1013 
1014     guint sample_count;
1015     guint i;
1016 
1017     g_object_get(sfz_file,
1018 		 "sample", &start_list,
1019 		 NULL);
1020 
1021     list = start_list;
1022 
1023     sample_count = g_list_length(start_list);
1024 
1025     sublevel_name = (gchar **) malloc((sample_count + 1) * sizeof(gchar*));
1026 
1027     for(i = 0; i < sample_count; i++){
1028       gchar *str;
1029 
1030       g_object_get(list->data,
1031 		   "filename", &str,
1032 		   NULL);
1033       sublevel_name[i] = str;
1034 
1035       list = list->next;
1036     }
1037 
1038     sublevel_name[i] = NULL;
1039 
1040     g_list_free_full(start_list,
1041 		     g_object_unref);
1042 
1043     return(sublevel_name);
1044   }
1045   };
1046 
1047   return(NULL);
1048 }
1049 
1050 guint
ags_sfz_file_level_up(AgsSoundContainer * sound_container,guint level_count)1051 ags_sfz_file_level_up(AgsSoundContainer *sound_container,
1052 		      guint level_count)
1053 {
1054   AgsSFZFile *sfz_file;
1055 
1056   guint retval;
1057 
1058   GRecMutex *sfz_file_mutex;
1059 
1060   if(level_count == 0){
1061     return(0);
1062   }
1063 
1064   sfz_file = AGS_SFZ_FILE(sound_container);
1065 
1066   /* get sfz_file mutex */
1067   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
1068 
1069   /* check boundaries */
1070   if(ags_sound_container_get_nesting_level(AGS_SOUND_CONTAINER(sfz_file)) >= level_count){
1071     /* level up */
1072     g_rec_mutex_lock(sfz_file_mutex);
1073 
1074     retval = level_count;
1075 
1076     sfz_file->nesting_level -= level_count;
1077 
1078     g_rec_mutex_unlock(sfz_file_mutex);
1079   }else{
1080     /* level up */
1081     g_rec_mutex_lock(sfz_file_mutex);
1082 
1083     retval = sfz_file->nesting_level;
1084 
1085     sfz_file->nesting_level = 0;
1086 
1087     g_rec_mutex_unlock(sfz_file_mutex);
1088   }
1089 
1090   return(retval);
1091 }
1092 
1093 guint
ags_sfz_file_select_level_by_id(AgsSoundContainer * sound_container,gchar * level_id)1094 ags_sfz_file_select_level_by_id(AgsSoundContainer *sound_container,
1095 				gchar *level_id)
1096 {
1097   return(0);
1098 }
1099 
1100 guint
ags_sfz_file_select_level_by_index(AgsSoundContainer * sound_container,guint level_index)1101 ags_sfz_file_select_level_by_index(AgsSoundContainer *sound_container,
1102 				   guint level_index)
1103 {
1104   AgsSFZFile *sfz_file;
1105 
1106   guint sublevel;
1107   guint retval;
1108 
1109   GRecMutex *sfz_file_mutex;
1110 
1111   sfz_file = AGS_SFZ_FILE(sound_container);
1112 
1113   /* get sfz_file mutex */
1114   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
1115 
1116   /* sublevel */
1117   sublevel = ags_sound_container_get_nesting_level(AGS_SOUND_CONTAINER(sfz_file));
1118   retval = 0;
1119 
1120   switch(sublevel){
1121   case AGS_SFZ_LEVEL_FILENAME:
1122   {
1123     if(ags_sfz_file_select_sample(sfz_file, level_index)){
1124       retval = AGS_SFZ_LEVEL_FILENAME;
1125     }
1126   }
1127   break;
1128   case AGS_SFZ_LEVEL_SAMPLE:
1129   {
1130     retval = AGS_SFZ_LEVEL_SAMPLE;
1131   }
1132   break;
1133   };
1134 
1135   return(retval);
1136 }
1137 
1138 GList*
ags_sfz_file_get_resource_all(AgsSoundContainer * sound_container)1139 ags_sfz_file_get_resource_all(AgsSoundContainer *sound_container)
1140 {
1141   AgsSFZFile *sfz_file;
1142 
1143   GList *resource;
1144 
1145   sfz_file = AGS_SFZ_FILE(sound_container);
1146 
1147   resource = NULL;
1148 
1149   //TODO:JK: implement me
1150 
1151   return(resource);
1152 }
1153 
1154 GList*
ags_sfz_file_get_resource_by_name(AgsSoundContainer * sound_container,gchar * resource_name)1155 ags_sfz_file_get_resource_by_name(AgsSoundContainer *sound_container,
1156 				  gchar *resource_name)
1157 {
1158   //TODO:JK: implement me
1159 
1160   return(NULL);
1161 }
1162 
1163 GList*
ags_sfz_file_get_resource_by_index(AgsSoundContainer * sound_container,guint resource_index)1164 ags_sfz_file_get_resource_by_index(AgsSoundContainer *sound_container,
1165 				   guint resource_index)
1166 {
1167   //TODO:JK: implement me
1168 
1169   return(NULL);
1170 }
1171 
1172 GList*
ags_sfz_file_get_resource_current(AgsSoundContainer * sound_container)1173 ags_sfz_file_get_resource_current(AgsSoundContainer *sound_container)
1174 {
1175   AgsSFZFile *sfz_file;
1176 
1177   GList *sound_resource;
1178 
1179   GRecMutex *sfz_file_mutex;
1180 
1181   sfz_file = AGS_SFZ_FILE(sound_container);
1182 
1183   /* get sfz_file mutex */
1184   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
1185 
1186   /* get sound resource */
1187   sound_resource = NULL;
1188 
1189   g_object_get(sfz_file,
1190 	       "sample", &sound_resource,
1191 	       NULL);
1192 
1193   return(sound_resource);
1194 }
1195 
1196 void
ags_sfz_file_close(AgsSoundContainer * sound_container)1197 ags_sfz_file_close(AgsSoundContainer *sound_container)
1198 {
1199   //TODO:JK: implement me
1200 }
1201 
1202 /**
1203  * ags_sfz_file_test_flags:
1204  * @sfz_file: the #AgsSFZFile
1205  * @flags: the flags
1206  *
1207  * Test @flags to be set on @sfz_file.
1208  *
1209  * Returns: %TRUE if flags are set, else %FALSE
1210  *
1211  * Since: 3.0.0
1212  */
1213 gboolean
ags_sfz_file_test_flags(AgsSFZFile * sfz_file,guint flags)1214 ags_sfz_file_test_flags(AgsSFZFile *sfz_file, guint flags)
1215 {
1216   gboolean retval;
1217 
1218   GRecMutex *sfz_file_mutex;
1219 
1220   if(!AGS_IS_SFZ_FILE(sfz_file)){
1221     return(FALSE);
1222   }
1223 
1224   /* get sfz_file mutex */
1225   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
1226 
1227   /* test */
1228   g_rec_mutex_lock(sfz_file_mutex);
1229 
1230   retval = (flags & (sfz_file->flags)) ? TRUE: FALSE;
1231 
1232   g_rec_mutex_unlock(sfz_file_mutex);
1233 
1234   return(retval);
1235 }
1236 
1237 /**
1238  * ags_sfz_file_set_flags:
1239  * @sfz_file: the #AgsSFZFile
1240  * @flags: see #AgsSFZFileFlags-enum
1241  *
1242  * Enable a feature of @sfz_file.
1243  *
1244  * Since: 3.0.0
1245  */
1246 void
ags_sfz_file_set_flags(AgsSFZFile * sfz_file,guint flags)1247 ags_sfz_file_set_flags(AgsSFZFile *sfz_file, guint flags)
1248 {
1249   GRecMutex *sfz_file_mutex;
1250 
1251   if(!AGS_IS_SFZ_FILE(sfz_file)){
1252     return;
1253   }
1254 
1255   /* get sfz_file mutex */
1256   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
1257 
1258   //TODO:JK: add more?
1259 
1260   /* set flags */
1261   g_rec_mutex_lock(sfz_file_mutex);
1262 
1263   sfz_file->flags |= flags;
1264 
1265   g_rec_mutex_unlock(sfz_file_mutex);
1266 }
1267 
1268 /**
1269  * ags_sfz_file_unset_flags:
1270  * @sfz_file: the #AgsSFZFile
1271  * @flags: see #AgsSFZFileFlags-enum
1272  *
1273  * Disable a feature of @sfz_file.
1274  *
1275  * Since: 3.0.0
1276  */
1277 void
ags_sfz_file_unset_flags(AgsSFZFile * sfz_file,guint flags)1278 ags_sfz_file_unset_flags(AgsSFZFile *sfz_file, guint flags)
1279 {
1280   GRecMutex *sfz_file_mutex;
1281 
1282   if(!AGS_IS_SFZ_FILE(sfz_file)){
1283     return;
1284   }
1285 
1286   /* get sfz_file mutex */
1287   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
1288 
1289   //TODO:JK: add more?
1290 
1291   /* unset flags */
1292   g_rec_mutex_lock(sfz_file_mutex);
1293 
1294   sfz_file->flags &= (~flags);
1295 
1296   g_rec_mutex_unlock(sfz_file_mutex);
1297 }
1298 
1299 /**
1300  * ags_sfz_file_select_sample:
1301  * @sfz_file: the #AgsSFZFile
1302  * @sample_index: the sample index
1303  *
1304  * Select sample.
1305  *
1306  * Returns: %TRUE on success, else %FALSE on failure
1307  *
1308  * Since: 3.0.0
1309  */
1310 gboolean
ags_sfz_file_select_sample(AgsSFZFile * sfz_file,guint sample_index)1311 ags_sfz_file_select_sample(AgsSFZFile *sfz_file,
1312 			   guint sample_index)
1313 {
1314   GList *start_list, *list;
1315 
1316   gboolean success;
1317 
1318   GRecMutex *sfz_file_mutex;
1319 
1320   if(!AGS_IS_SFZ_FILE(sfz_file)){
1321     return(FALSE);
1322   }
1323 
1324   /* get sfz file mutex */
1325   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
1326 
1327   success = FALSE;
1328 
1329   g_object_get(sfz_file,
1330 	       "sample", &start_list,
1331 	       NULL);
1332 
1333   if(start_list != NULL){
1334     list = start_list;
1335 
1336     if(sample_index < g_list_length(start_list)){
1337       gchar *filename;
1338 
1339       success = TRUE;
1340 
1341       list = g_list_nth(start_list,
1342 			sample_index);
1343 
1344       /* selected index and name */
1345       filename = NULL;
1346 
1347       g_object_get(list->data,
1348 		   "filename", &filename,
1349 		   NULL);
1350 
1351       g_rec_mutex_lock(sfz_file_mutex);
1352 
1353       sfz_file->index_selected[AGS_SFZ_LEVEL_SAMPLE] = sample_index;
1354 
1355       g_free(sfz_file->name_selected[AGS_SFZ_LEVEL_SAMPLE]);
1356 
1357       sfz_file->name_selected[AGS_SFZ_LEVEL_SAMPLE] = filename;
1358 
1359       /* container */
1360       sfz_file->current_sample = (AgsSFZSample *) list->data;
1361 
1362       g_rec_mutex_unlock(sfz_file_mutex);
1363     }
1364 
1365     g_list_free_full(start_list,
1366 		     g_object_unref);
1367   }
1368 
1369   return(success);
1370 }
1371 
1372 /**
1373  * ags_sfz_file_get_range:
1374  * @sfz_file: the #AgsSFZFile
1375  * @hikey: (out): the return location of key high
1376  * @lokey: (out): the return location of key low
1377  *
1378  * Get range of @sfz_file, set return location @hikey and @lokey.
1379  *
1380  * Since: 3.0.0
1381  */
1382 void
ags_sfz_file_get_range(AgsSFZFile * sfz_file,glong * hikey,glong * lokey)1383 ags_sfz_file_get_range(AgsSFZFile *sfz_file,
1384 		       glong *hikey, glong *lokey)
1385 {
1386   GList *start_group, *group;
1387   GList *start_region, *region;
1388 
1389   gchar *str;
1390 
1391   glong upper, lower;
1392   glong value;
1393   int retval;
1394 
1395   if(!AGS_IS_SFZ_FILE(sfz_file)){
1396     return;
1397   }
1398 
1399   g_object_get(sfz_file,
1400 	       "group", &start_group,
1401 	       "region", &start_region,
1402 	       NULL);
1403 
1404   upper = 0;
1405   lower = AGS_SFZ_FILE_LOOP_MAX;
1406 
1407   group = start_group;
1408 
1409   while(group != NULL){
1410     /* hikey */
1411     str = ags_sfz_group_lookup_control(group->data,
1412 				       "hikey");
1413 
1414     value = 0;
1415 
1416     if(str != NULL){
1417       retval = sscanf(str, "%3ld", &value);
1418 
1419       if(retval <= 0){
1420 	glong tmp;
1421 	guint tmp_retval;
1422 
1423 	tmp_retval = ags_diatonic_scale_note_to_midi_key(str,
1424 							 &tmp);
1425 
1426 	if(retval > 0){
1427 	  value = tmp;
1428 	}
1429       }
1430     }
1431 
1432     if(value > upper){
1433       upper = value;
1434     }
1435 
1436     /* lokey */
1437     str = ags_sfz_group_lookup_control(group->data,
1438 				       "hikey");
1439 
1440     value = AGS_SFZ_FILE_LOOP_MAX;
1441 
1442     if(str != NULL){
1443       retval = sscanf(str, "%3ld", &value);
1444 
1445       if(retval <= 0){
1446 	glong tmp;
1447 	guint tmp_retval;
1448 
1449 	tmp_retval = ags_diatonic_scale_note_to_midi_key(str,
1450 							 &tmp);
1451 
1452 	if(retval > 0){
1453 	  value = tmp;
1454 	}
1455       }
1456     }
1457 
1458     if(value < lower){
1459       lower = value;
1460     }
1461 
1462     /* iterate */
1463     group = group->next;
1464   }
1465 
1466   region = start_region;
1467 
1468   while(region != NULL){
1469     /* hikey */
1470     str = ags_sfz_region_lookup_control(region->data,
1471 					"hikey");
1472 
1473     value = 0;
1474 
1475     if(str != NULL){
1476       retval = sscanf(str, "%3ld", &value);
1477 
1478       if(retval <= 0){
1479 	glong tmp;
1480 	guint tmp_retval;
1481 
1482 	tmp_retval = ags_diatonic_scale_note_to_midi_key(str,
1483 							 &tmp);
1484 
1485 	if(retval > 0){
1486 	  value = tmp;
1487 	}
1488       }
1489     }
1490 
1491     if(value > upper){
1492       upper = value;
1493     }
1494 
1495     /* lokey */
1496     str = ags_sfz_region_lookup_control(region->data,
1497 					"hikey");
1498 
1499     value = AGS_SFZ_FILE_LOOP_MAX;
1500 
1501     if(str != NULL){
1502       retval = sscanf(str, "%3ld", &value);
1503 
1504       if(retval <= 0){
1505 	glong tmp;
1506 	guint tmp_retval;
1507 
1508 	tmp_retval = ags_diatonic_scale_note_to_midi_key(str,
1509 							 &tmp);
1510 
1511 	if(retval > 0){
1512 	  value = tmp;
1513 	}
1514       }
1515     }
1516 
1517     if(value < lower){
1518       lower = value;
1519     }
1520 
1521     /* iterate */
1522     region = region->next;
1523   }
1524 
1525   /* set return location */
1526   if(lokey < hikey){
1527     if(lokey != NULL){
1528       lokey[0] = lower;
1529     }
1530 
1531     if(hikey != NULL){
1532       hikey[0] = upper;
1533     }
1534   }else{
1535     if(lokey != NULL){
1536       lokey[0] = 49;
1537     }
1538 
1539     if(hikey != NULL){
1540       hikey[0] = 49;
1541     }
1542   }
1543 }
1544 
1545 /**
1546  * ags_sfz_file_check_suffix:
1547  * @filename: the filename
1548  *
1549  * Check @filename's suffix to be supported.
1550  *
1551  * Returns: %TRUE if supported, else %FALSE
1552  *
1553  * Since: 3.0.0
1554  */
1555 gboolean
ags_sfz_file_check_suffix(gchar * filename)1556 ags_sfz_file_check_suffix(gchar *filename)
1557 {
1558   if(g_str_has_suffix(filename, ".sfz")){
1559     return(TRUE);
1560   }
1561 
1562   return(FALSE);
1563 }
1564 
1565 gchar*
ags_sfz_file_parse_skip_comments_and_blanks(gchar * buffer,gsize buffer_length,gchar ** iter)1566 ags_sfz_file_parse_skip_comments_and_blanks(gchar *buffer, gsize buffer_length,
1567 					    gchar **iter)
1568 {
1569   gchar *look_ahead;
1570   gchar *next;
1571 
1572   if(iter == NULL){
1573     return(NULL);
1574   }
1575 
1576   look_ahead = *iter;
1577 
1578   if(look_ahead == NULL){
1579     return(NULL);
1580   }
1581 
1582   /* skip whitespaces and comments */
1583   for(; (look_ahead < &(buffer[buffer_length])) && *look_ahead != '\0';){
1584     /* skip comments */
1585     if(buffer == look_ahead){
1586       if(look_ahead + 1 < &(buffer[buffer_length]) && buffer[0] == '/' && buffer[1] == '/'){
1587 	next = strchr(look_ahead, '\n');
1588 
1589 	if(next != NULL){
1590 	  look_ahead = next + 1;
1591 	}else{
1592 	  look_ahead = &(buffer[buffer_length]);
1593 
1594 	  break;
1595 	}
1596 
1597 	continue;
1598       }
1599     }else if(buffer[look_ahead - buffer - 1] == '\n' && look_ahead + 1 < &(buffer[buffer_length]) && look_ahead[0] == '/' && look_ahead[1] == '/'){
1600       next = strchr(look_ahead, '\n');
1601 
1602       if(next != NULL){
1603 	look_ahead = next + 1;
1604       }else{
1605 	look_ahead = &(buffer[buffer_length]);
1606 
1607 	break;
1608       }
1609 
1610       continue;
1611     }
1612 
1613     /* spaces */
1614     if(!(look_ahead[0] == ' ' || look_ahead[0] == '\t' || look_ahead[0] == '\n' || look_ahead[0] == '\r')){
1615       break;
1616     }else{
1617       look_ahead++;
1618     }
1619   }
1620 
1621   return(look_ahead);
1622 }
1623 
1624 /**
1625  * ags_sfz_file_parse:
1626  * @sfz_file: the #AgsSFZFile
1627  *
1628  * Parse @sfz_file.
1629  *
1630  * Since: 3.0.0
1631  */
1632 void
ags_sfz_file_parse(AgsSFZFile * sfz_file)1633 ags_sfz_file_parse(AgsSFZFile *sfz_file)
1634 {
1635   AgsSFZGroup *current_group;
1636   AgsSFZRegion *current_region;
1637   AgsSFZSample *current_sample;
1638 
1639   FILE *file;
1640 
1641   struct stat *sb;
1642 
1643   GList *start_list, *list;
1644 
1645   regmatch_t match_arr[2];
1646 
1647   gchar *filename;
1648   gchar *buffer, *iter;
1649 
1650   size_t n_read;
1651   gint nth_group;
1652   gint nth_region;
1653   gboolean group_active;
1654   gboolean region_active;
1655   gboolean sample_active;
1656 
1657   GRecMutex *sfz_file_mutex;
1658 
1659   static regex_t opcode_regex;
1660   static regex_t opcode_regex_next;
1661 
1662   static const gchar *opcode_pattern = "^([a-zA-Z_]+)\\=";
1663   static const gchar *opcode_pattern_next = "([a-zA-Z_]+)\\=";
1664 
1665   static const size_t max_matches = 2;
1666   static gboolean regex_compiled = FALSE;
1667 
1668   if(!AGS_IS_SFZ_FILE(sfz_file)){
1669     return;
1670   }
1671 
1672   g_message("SFZ parse");
1673 
1674   /* get sfz_file mutex */
1675   sfz_file_mutex = AGS_SFZ_FILE_GET_OBJ_MUTEX(sfz_file);
1676 
1677   /* stat file */
1678   g_object_get(sfz_file,
1679 	       "filename", &filename,
1680 	       NULL);
1681 
1682   sb = (struct stat *) malloc(sizeof(struct stat));
1683   stat(filename,
1684        sb);
1685 
1686   if(sb->st_size == 0){
1687     g_free(filename);
1688 
1689     free(sb);
1690 
1691     return;
1692   }
1693 
1694   g_free(filename);
1695 
1696   /* read SFZ */
1697   g_rec_mutex_lock(sfz_file_mutex);
1698 
1699   file = sfz_file->file;
1700 
1701   g_rec_mutex_unlock(sfz_file_mutex);
1702 
1703   buffer = (gchar *) malloc((sb->st_size + 1) * sizeof(gchar));
1704 
1705   if(buffer == NULL){
1706     free(sb);
1707 
1708     return;
1709   }
1710 
1711   n_read = fread(buffer, sizeof(gchar), sb->st_size, file);
1712 
1713   if(n_read != sb->st_size){
1714     g_critical("number of read bytes doesn't match buffer size");
1715   }
1716 
1717   buffer[sb->st_size] = '\0';
1718 
1719   iter = buffer;
1720 
1721   current_group = NULL;
1722   current_region = NULL;
1723   current_sample = NULL;
1724 
1725   nth_group = -1;
1726   nth_region = -1;
1727 
1728   group_active = FALSE;
1729   region_active = FALSE;
1730   sample_active = FALSE;
1731 
1732   g_mutex_lock(&regex_mutex);
1733 
1734   if(!regex_compiled){
1735     regex_compiled = TRUE;
1736 
1737     ags_regcomp(&opcode_regex, opcode_pattern, REG_EXTENDED);
1738     ags_regcomp(&opcode_regex_next, opcode_pattern_next, REG_EXTENDED);
1739   }
1740 
1741   g_mutex_unlock(&regex_mutex);
1742 
1743   do{
1744     /* skip blanks and comments */
1745     iter = ags_sfz_file_parse_skip_comments_and_blanks(buffer, sb->st_size,
1746 						       &iter);
1747 
1748     if(iter >= &(buffer[sb->st_size])){
1749       break;
1750     }
1751 
1752     if(!g_ascii_strncasecmp(iter,
1753 			    "<group>",
1754 			    7)){
1755       g_message("SFZ group");
1756 
1757       nth_group++;
1758 
1759       group_active = TRUE;
1760       region_active = FALSE;
1761       sample_active = FALSE;
1762 
1763       current_group = ags_sfz_group_new();
1764       g_object_set(sfz_file,
1765 		   "group", current_group,
1766 		   NULL);
1767 
1768       iter += 7;
1769     }else if(!g_ascii_strncasecmp(iter,
1770 				  "<region>",
1771 				  8)){
1772       g_message("SFZ region");
1773 
1774       nth_region++;
1775 
1776       region_active = TRUE;
1777       sample_active = FALSE;
1778 
1779       current_region = ags_sfz_region_new();
1780       g_object_set(sfz_file,
1781 		   "region", current_region,
1782 		   NULL);
1783 
1784       iter += 8;
1785     }else if(ags_regexec(&opcode_regex, iter, max_matches, match_arr, 0) == 0){
1786       gchar *opcode;
1787       gchar *str;
1788       gchar *next, *tmp0_next, *tmp1_next;
1789 
1790       iter += match_arr[1].rm_so;
1791 
1792       opcode = g_strndup(iter,
1793 			 match_arr[1].rm_eo - match_arr[1].rm_so);
1794 
1795       iter += strlen(opcode) + 1;
1796 
1797       if(ags_regexec(&opcode_regex_next, iter, max_matches, match_arr, 0) == 0){
1798 	tmp0_next = strchr(iter, '\n');
1799 	tmp1_next = strchr(iter, '\r');
1800 
1801 	if((tmp0_next != NULL || tmp1_next != NULL) &&
1802 	   ((tmp0_next != NULL && tmp0_next < iter + match_arr[1].rm_so) || (tmp1_next != NULL && tmp1_next < iter + match_arr[1].rm_so))){
1803 	  if(tmp0_next != NULL && (tmp1_next == NULL || tmp0_next < tmp1_next)){
1804 	    next = tmp0_next;
1805 	  }else{
1806 	    next = tmp1_next;
1807 	  }
1808 	}else{
1809 	  next = iter + match_arr[1].rm_so;
1810 	}
1811       }else{
1812 	tmp0_next = strchr(iter, '\n');
1813 	tmp1_next = strchr(iter, '\r');
1814 
1815 	if(tmp0_next != NULL || tmp1_next != NULL){
1816 	  if(tmp0_next != NULL && (tmp1_next == NULL || tmp0_next < tmp1_next)){
1817 	    next = tmp0_next;
1818 	  }else{
1819 	    next = tmp1_next;
1820 	  }
1821 	}else{
1822 	  next = &(buffer[sb->st_size]);
1823 	}
1824       }
1825 
1826       while(next > iter){
1827 	if((next - 1)[0] == ' '){
1828 	  next--;
1829 	}else{
1830 	  break;
1831 	}
1832       }
1833 
1834       str = g_strndup(iter,
1835 		      next - iter);
1836 
1837       iter = next;
1838 
1839       g_message("opcode - %s=%s", opcode, str);
1840 
1841       if(!g_ascii_strncasecmp(opcode,
1842 			      "sample",
1843 			      6)){
1844 	gchar *filename;
1845 	gchar *tmp;
1846 
1847 	gboolean success;
1848 
1849 	tmp = str;
1850 
1851 	while((tmp = strchr(tmp, '\\')) != NULL){
1852 	  tmp[0] = '/';
1853 
1854 	  tmp++;
1855 	}
1856 
1857 	sample_active = TRUE;
1858 
1859 	if(g_path_is_absolute(str)){
1860 	  filename = g_strdup(str);
1861 	}else{
1862 	  gchar *path;
1863 
1864 	  path = g_path_get_dirname(sfz_file->filename);
1865 
1866 	  filename = g_strdup_printf("%s/%s",
1867 				     path,
1868 				     str);
1869 	}
1870 
1871 	current_sample = ags_sfz_sample_new();
1872 	g_object_set(current_sample,
1873 		     "group", current_group,
1874 		     "region", current_region,
1875 		     NULL);
1876 
1877 	success = ags_sound_resource_open(AGS_SOUND_RESOURCE(current_sample),
1878 					  filename);
1879 
1880 	if(!success){
1881 	  g_message("failed to open %s", filename);
1882 	}
1883 
1884 	g_object_set(sfz_file,
1885 		     "sample", current_sample,
1886 		     NULL);
1887 
1888 	if(region_active){
1889 	  g_object_set(current_region,
1890 		       "sample", current_sample,
1891 		       NULL);
1892 	}else if(group_active){
1893 	  g_object_set(current_group,
1894 		       "sample", current_sample,
1895  		       NULL);
1896 	}
1897 
1898 	g_free(filename);
1899       }
1900 
1901       if(region_active){
1902 	ags_sfz_region_insert_control(current_region,
1903 				      opcode, str);
1904       }else if(group_active){
1905 	ags_sfz_group_insert_control(current_group,
1906 				     opcode, str);
1907       }else{
1908 	g_warning("SFZ neither group nor region defined");
1909       }
1910     }else{
1911       /* bad byte */
1912       iter++;
1913     }
1914   }while(iter < &(buffer[sb->st_size]));
1915 
1916   free(sb);
1917   free(buffer);
1918 
1919   /* apply loop start/end */
1920   g_object_get(sfz_file,
1921 	       "sample", &start_list,
1922 	       NULL);
1923 
1924   list = start_list;
1925 
1926   while(list != NULL){
1927     AgsSFZGroup *group;
1928     AgsSFZRegion *region;
1929     AgsSFZSample *sample;
1930 
1931     guint loop_start, loop_end;
1932 
1933     sample = (AgsSFZSample *) list->data;
1934 
1935     loop_start = 0;
1936     loop_end = 0;
1937 
1938     g_object_get(sample,
1939 		 "group", &group,
1940 		 "region", &region,
1941 		 NULL);
1942 
1943     /* check group */
1944     if(group != NULL){
1945       gchar *str;
1946 
1947       str = ags_sfz_group_lookup_control(group,
1948 					 "loop_start");
1949 
1950       if(str != NULL){
1951 	loop_start = g_ascii_strtoull(str,
1952 				      NULL,
1953 				      10);
1954 
1955 	g_free(str);
1956       }
1957 
1958       str = ags_sfz_group_lookup_control(group,
1959 					 "loop_end");
1960 
1961       if(str != NULL){
1962 	loop_end = g_ascii_strtoull(str,
1963 				    NULL,
1964 				    10);
1965 
1966 	g_free(str);
1967       }
1968     }
1969 
1970     /* check region */
1971     if(region != NULL){
1972       gchar *str;
1973 
1974       str = ags_sfz_region_lookup_control(region,
1975 					 "loop_start");
1976 
1977       if(str != NULL){
1978 	loop_start = g_ascii_strtoull(str,
1979 				      NULL,
1980 				      10);
1981 
1982 	g_free(str);
1983       }
1984 
1985       str = ags_sfz_region_lookup_control(region,
1986 					 "loop_end");
1987 
1988       if(str != NULL){
1989 	loop_end = g_ascii_strtoull(str,
1990 				    NULL,
1991 				    10);
1992 
1993 	g_free(str);
1994       }
1995     }
1996 
1997     g_object_set(sample,
1998 		 "loop-start", loop_start,
1999 		 "loop-end", loop_end,
2000 		 NULL);
2001 
2002     /* iterate */
2003     list = list->next;
2004   }
2005 
2006   g_list_free_full(start_list,
2007 		   g_object_unref);
2008 }
2009 
2010 /**
2011  * ags_sfz_file_new:
2012  *
2013  * Creates an #AgsSFZFile.
2014  *
2015  * Returns: an empty #AgsSFZFile.
2016  *
2017  * Since: 3.0.0
2018  */
2019 AgsSFZFile*
ags_sfz_file_new()2020 ags_sfz_file_new()
2021 {
2022   AgsSFZFile *sfz_file;
2023 
2024   sfz_file = (AgsSFZFile *) g_object_new(AGS_TYPE_SFZ_FILE,
2025 					 NULL);
2026 
2027   return(sfz_file);
2028 }
2029