1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2019 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 Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (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 Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with GSequencer.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <ags/audio/osc/controller/ags_osc_export_controller.h>
21 
22 #include <ags/audio/ags_sound_provider.h>
23 #include <ags/audio/ags_audio.h>
24 
25 #include <ags/audio/task/ags_export_output.h>
26 
27 #include <ags/audio/osc/ags_osc_response.h>
28 #include <ags/audio/osc/ags_osc_buffer_util.h>
29 
30 #include <ags/i18n.h>
31 
32 void ags_osc_export_controller_class_init(AgsOscExportControllerClass *osc_export_controller);
33 void ags_osc_export_controller_init(AgsOscExportController *osc_export_controller);
34 void ags_osc_export_controller_dispose(GObject *gobject);
35 void ags_osc_export_controller_finalize(GObject *gobject);
36 
37 gpointer ags_osc_export_controller_real_do_export(AgsOscExportController *osc_export_controller,
38 						  AgsOscConnection *osc_connection,
39 						  unsigned char *message, guint message_size);
40 
41 /**
42  * SECTION:ags_osc_export_controller
43  * @short_description: OSC export controller
44  * @title: AgsOscExportController
45  * @section_id:
46  * @include: ags/audio/osc/controller/ags_osc_export_controller.h
47  *
48  * The #AgsOscExportController implements the OSC export controller.
49  */
50 
51 enum{
52   PROP_0,
53 };
54 
55 enum{
56   DO_EXPORT,
57   LAST_SIGNAL,
58 };
59 
60 static gpointer ags_osc_export_controller_parent_class = NULL;
61 static guint osc_export_controller_signals[LAST_SIGNAL];
62 
63 static GMutex regex_mutex;
64 
65 GType
ags_osc_export_controller_get_type()66 ags_osc_export_controller_get_type()
67 {
68   static volatile gsize g_define_type_id__volatile = 0;
69 
70   if(g_once_init_enter (&g_define_type_id__volatile)){
71     GType ags_type_osc_export_controller = 0;
72 
73     static const GTypeInfo ags_osc_export_controller_info = {
74       sizeof (AgsOscExportControllerClass),
75       NULL, /* base_init */
76       NULL, /* base_finalize */
77       (GClassInitFunc) ags_osc_export_controller_class_init,
78       NULL, /* class_finalize */
79       NULL, /* class_data */
80       sizeof (AgsOscExportController),
81       0,    /* n_preallocs */
82       (GInstanceInitFunc) ags_osc_export_controller_init,
83     };
84 
85     ags_type_osc_export_controller = g_type_register_static(AGS_TYPE_OSC_CONTROLLER,
86 							    "AgsOscExportController",
87 							    &ags_osc_export_controller_info,
88 							    0);
89 
90     g_once_init_leave(&g_define_type_id__volatile, ags_type_osc_export_controller);
91   }
92 
93   return g_define_type_id__volatile;
94 }
95 
96 void
ags_osc_export_controller_class_init(AgsOscExportControllerClass * osc_export_controller)97 ags_osc_export_controller_class_init(AgsOscExportControllerClass *osc_export_controller)
98 {
99   GObjectClass *gobject;
100   GParamSpec *param_spec;
101 
102   ags_osc_export_controller_parent_class = g_type_class_peek_parent(osc_export_controller);
103 
104   /* GObjectClass */
105   gobject = (GObjectClass *) osc_export_controller;
106 
107   gobject->dispose = ags_osc_export_controller_dispose;
108   gobject->finalize = ags_osc_export_controller_finalize;
109 
110   /* properties */
111 
112   /* AgsOscExportControllerClass */
113   osc_export_controller->do_export = ags_osc_export_controller_real_do_export;
114 
115   /* signals */
116   /**
117    * AgsOscExportController::do-export:
118    * @osc_export_controller: the #AgsOscExportController
119    * @osc_connection: the #AgsOscConnection
120    * @message: the message received
121    * @message_size: the message size
122    *
123    * The ::do-export signal is emited during get data of export controller.
124    *
125    * Returns: the #AgsOscResponse
126    *
127    * Since: 3.0.0
128    */
129   osc_export_controller_signals[DO_EXPORT] =
130     g_signal_new("do-export",
131 		 G_TYPE_FROM_CLASS(osc_export_controller),
132 		 G_SIGNAL_RUN_LAST,
133 		 G_STRUCT_OFFSET(AgsOscExportControllerClass, do_export),
134 		 NULL, NULL,
135 		 ags_cclosure_marshal_POINTER__OBJECT_POINTER_UINT,
136 		 G_TYPE_POINTER, 3,
137 		 G_TYPE_OBJECT,
138 		 G_TYPE_POINTER,
139 		 G_TYPE_UINT);
140 }
141 
142 void
ags_osc_export_controller_init(AgsOscExportController * osc_export_controller)143 ags_osc_export_controller_init(AgsOscExportController *osc_export_controller)
144 {
145   g_object_set(osc_export_controller,
146 	       "context-path", "/export",
147 	       NULL);
148 }
149 
150 void
ags_osc_export_controller_dispose(GObject * gobject)151 ags_osc_export_controller_dispose(GObject *gobject)
152 {
153   AgsOscExportController *osc_export_controller;
154 
155   osc_export_controller = AGS_OSC_EXPORT_CONTROLLER(gobject);
156 
157   /* call parent */
158   G_OBJECT_CLASS(ags_osc_export_controller_parent_class)->dispose(gobject);
159 }
160 
161 void
ags_osc_export_controller_finalize(GObject * gobject)162 ags_osc_export_controller_finalize(GObject *gobject)
163 {
164   AgsOscExportController *osc_export_controller;
165 
166   osc_export_controller = AGS_OSC_EXPORT_CONTROLLER(gobject);
167 
168   /* call parent */
169   G_OBJECT_CLASS(ags_osc_export_controller_parent_class)->finalize(gobject);
170 }
171 
172 gpointer
ags_osc_export_controller_real_do_export(AgsOscExportController * osc_export_controller,AgsOscConnection * osc_connection,unsigned char * message,guint message_size)173 ags_osc_export_controller_real_do_export(AgsOscExportController *osc_export_controller,
174 					 AgsOscConnection *osc_connection,
175 					 unsigned char *message, guint message_size)
176 {
177   AgsOscResponse *osc_response;
178 
179   AgsTaskLauncher *task_launcher;
180   AgsTask *task;
181 
182   AgsApplicationContext *application_context;
183 
184   GList *start_response;
185   GList *start_list, *list;
186 
187   gchar *type_tag;
188   gchar *path;
189   gchar *filename;
190 
191   guint format;
192   guint64 tic;
193   gboolean live_performance;
194   guint length;
195   guint path_offset;
196   gboolean success;
197 
198   start_response = NULL;
199 
200   /* read type tag */
201   ags_osc_buffer_util_get_string(message + 8,
202 				 &type_tag, NULL);
203 
204   success = (type_tag != NULL &&
205 	     !strncmp(type_tag, ",ssihs", 6)) ? TRUE: FALSE;
206 
207   if(success){
208     success = (!strncmp(type_tag + 6, "T", 2) || strncmp(type_tag + 6, "F", 2)) ? TRUE: FALSE;
209   }
210 
211   if(!success){
212     osc_response = ags_osc_response_new();
213     start_response = g_list_prepend(start_response,
214 				    osc_response);
215 
216     ags_osc_response_set_flags(osc_response,
217 			       AGS_OSC_RESPONSE_ERROR);
218 
219     g_object_set(osc_response,
220 		 "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MALFORMED_REQUEST,
221 		 NULL);
222 
223     if(type_tag != NULL){
224       free(type_tag);
225     }
226 
227     return(start_response);
228   }
229 
230   /* read path */
231   ags_osc_buffer_util_get_string(message + 12,
232 				 &path, NULL);
233 
234   /* check argument */
235   if(path == NULL){
236     osc_response = ags_osc_response_new();
237     start_response = g_list_prepend(start_response,
238 				    osc_response);
239 
240     ags_osc_response_set_flags(osc_response,
241 			       AGS_OSC_RESPONSE_ERROR);
242 
243     g_object_set(osc_response,
244 		 "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MALFORMED_REQUEST,
245 		 NULL);
246 
247     free(type_tag);
248 
249     return(start_response);
250   }
251 
252   length = 4 * (guint) ceil((double) (strlen(path) + 1) / 4.0);
253 
254   /* read filename */
255   ags_osc_buffer_util_get_string(message + 12 + length,
256 				 &filename, NULL);
257 
258   /* check argument */
259   if(filename == NULL){
260     osc_response = ags_osc_response_new();
261     start_response = g_list_prepend(start_response,
262 				    osc_response);
263 
264     ags_osc_response_set_flags(osc_response,
265 			       AGS_OSC_RESPONSE_ERROR);
266 
267     g_object_set(osc_response,
268 		 "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MALFORMED_REQUEST,
269 		 NULL);
270 
271     free(type_tag);
272     free(path);
273 
274     return(start_response);
275   }
276 
277   length += 4 * (guint) ceil((double) (strlen(filename) + 1) / 4.0);
278 
279   /* read format */
280   ags_osc_buffer_util_get_int32(message + 12 + length,
281 				&format);
282 
283   /* check argument */
284   if(format == 0){
285     osc_response = ags_osc_response_new();
286     start_response = g_list_prepend(start_response,
287 				    osc_response);
288 
289     ags_osc_response_set_flags(osc_response,
290 			       AGS_OSC_RESPONSE_ERROR);
291 
292     g_object_set(osc_response,
293 		 "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MALFORMED_REQUEST,
294 		 NULL);
295 
296     free(type_tag);
297     free(path);
298     free(filename);
299 
300     return(start_response);
301   }
302 
303   /* read tic */
304   ags_osc_buffer_util_get_int64(message + 16 + length,
305 				&tic);
306 
307   /* check argument */
308   if(tic == 0){
309     osc_response = ags_osc_response_new();
310     start_response = g_list_prepend(start_response,
311 				    osc_response);
312 
313     ags_osc_response_set_flags(osc_response,
314 			       AGS_OSC_RESPONSE_ERROR);
315 
316     g_object_set(osc_response,
317 		 "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MALFORMED_REQUEST,
318 		 NULL);
319 
320     free(type_tag);
321     free(path);
322     free(filename);
323 
324     return(start_response);
325   }
326 
327   /* read live export */
328   live_performance = (type_tag[6] == 'T') ? TRUE: FALSE;
329 
330   /* get sound provider */
331   application_context = ags_application_context_get_instance();
332 
333   task_launcher = ags_concurrency_provider_get_task_launcher(AGS_CONCURRENCY_PROVIDER(application_context));
334 
335   path_offset = 0;
336 
337   if(!strncmp(path, "/AgsSoundProvider", 17)){
338     path_offset += 17;
339 
340     if(!strncmp(path + path_offset, "/AgsSoundcard", 13)){
341       AgsExportThread *export_thread;
342 
343       AgsThread *main_loop;
344 
345       GList *start_soundcard, *soundcard;
346 
347       regmatch_t match_arr[2];
348 
349       static regex_t single_access_regex;
350       static regex_t range_access_regex;
351       static regex_t voluntary_access_regex;
352       static regex_t more_access_regex;
353       static regex_t wildcard_access_regex;
354 
355       static gboolean regex_compiled = FALSE;
356 
357       static const gchar *single_access_pattern = "^\\[([0-9]+)\\]";
358       static const gchar *range_access_pattern = "^\\[([0-9]+)\\-([0-9]+)\\]";
359       static const gchar *voluntary_access_pattern = "^\\[(\\?)\\]";
360       static const gchar *more_access_pattern = "^\\[(\\+)\\]";
361       static const gchar *wildcard_access_pattern = "^\\[(\\*)\\]";
362 
363       static const size_t max_matches = 2;
364       static const size_t index_max_matches = 1;
365 
366       path_offset += 13;
367 
368       /* compile regex */
369       g_mutex_lock(&regex_mutex);
370 
371       if(!regex_compiled){
372 	regex_compiled = TRUE;
373 
374 	ags_regcomp(&single_access_regex, single_access_pattern, REG_EXTENDED);
375 	ags_regcomp(&range_access_regex, range_access_pattern, REG_EXTENDED);
376 	ags_regcomp(&voluntary_access_regex, voluntary_access_pattern, REG_EXTENDED);
377 	ags_regcomp(&more_access_regex, more_access_pattern, REG_EXTENDED);
378 	ags_regcomp(&wildcard_access_regex, wildcard_access_pattern, REG_EXTENDED);
379       }
380 
381       g_mutex_unlock(&regex_mutex);
382 
383       main_loop = ags_concurrency_provider_get_main_loop(AGS_CONCURRENCY_PROVIDER(application_context));
384 
385       export_thread = (AgsExportThread *) ags_thread_find_type(main_loop,
386 							       AGS_TYPE_EXPORT_THREAD);
387 
388       soundcard =
389 	start_soundcard = ags_sound_provider_get_soundcard(AGS_SOUND_PROVIDER(application_context));
390 
391       if(ags_regexec(&single_access_regex, path + path_offset, index_max_matches, match_arr, 0) == 0){
392 	AgsExportOutput *export_output;
393 
394 	AgsExportThread *current_export_thread;
395 
396 	GObject *current;
397 
398 	gchar *endptr;
399 
400 	guint i_stop;
401 
402 	endptr = NULL;
403 	i_stop = g_ascii_strtoull(path + path_offset + 1,
404 				  &endptr,
405 				  10);
406 
407 	current = g_list_nth_data(start_soundcard,
408 				  i_stop);
409 
410 	path_offset += ((endptr + 1) - (path + path_offset));
411 
412 	current_export_thread = ags_export_thread_find_soundcard(export_thread,
413 								 current);
414 
415 	export_output = ags_export_output_new(current_export_thread,
416 					      current,
417 					      filename,
418 					      tic,
419 					      live_performance);
420 
421 	ags_task_launcher_add_task(task_launcher,
422 				   (AgsTask *) export_output);
423 
424 	/* create response */
425 	osc_response = ags_osc_response_new();
426 	start_response = g_list_prepend(start_response,
427 					osc_response);
428 
429 	ags_osc_response_set_flags(osc_response,
430 				   AGS_OSC_RESPONSE_OK);
431       }else if(ags_regexec(&range_access_regex, path + path_offset, max_matches, match_arr, 0) == 0){
432 	AgsExportOutput *export_output;
433 
434 	AgsExportThread *current_export_thread;
435 
436 	gchar *endptr;
437 
438 	guint i;
439 	guint i_start, i_stop;
440 
441 	endptr = NULL;
442 	i_start = g_ascii_strtoull(path + path_offset + 1,
443 				   &endptr,
444 				   10);
445 
446 	i_stop = g_ascii_strtoull(endptr + 1,
447 				  &endptr,
448 				  10);
449 
450 	path_offset += ((endptr + 1) - (path + path_offset));
451 
452 	soundcard = g_list_nth(start_soundcard,
453 			       i_start);
454 
455 	for(i = i_start; i <= i_stop; i++){
456 	  current_export_thread = ags_export_thread_find_soundcard(export_thread,
457 								   soundcard->data);
458 
459 	  export_output = ags_export_output_new(current_export_thread,
460 						soundcard->data,
461 						filename,
462 						tic,
463 						live_performance);
464 
465 	  ags_task_launcher_add_task(task_launcher,
466 				      (AgsTask *) export_output);
467 
468 	  /* create response */
469 	  osc_response = ags_osc_response_new();
470 	  start_response = g_list_prepend(start_response,
471 					  osc_response);
472 
473 	  ags_osc_response_set_flags(osc_response,
474 				     AGS_OSC_RESPONSE_OK);
475 
476 	  soundcard = soundcard->next;
477 	}
478       }else if(ags_regexec(&voluntary_access_regex, path + path_offset, index_max_matches, match_arr, 0) == 0){
479 	AgsExportOutput *export_output;
480 
481 	AgsExportThread *current_export_thread;
482 
483 	path_offset += 3;
484 
485 	if(start_soundcard != NULL){
486 	  current_export_thread = ags_export_thread_find_soundcard(export_thread,
487 								   start_soundcard->data);
488 
489 	  export_output = ags_export_output_new(current_export_thread,
490 						start_soundcard->data,
491 						filename,
492 						tic,
493 						live_performance);
494 
495 	  ags_task_launcher_add_task(task_launcher,
496 				      (AgsTask *) export_output);
497 
498 	  /* create response */
499 	  osc_response = ags_osc_response_new();
500 	  start_response = g_list_prepend(start_response,
501 					  osc_response);
502 
503 	  ags_osc_response_set_flags(osc_response,
504 				     AGS_OSC_RESPONSE_OK);
505 	}else{
506 	  osc_response = ags_osc_response_new();
507 	  start_response = g_list_prepend(start_response,
508 					  osc_response);
509 
510 	  ags_osc_response_set_flags(osc_response,
511 				     AGS_OSC_RESPONSE_OK);
512 
513 	  free(type_tag);
514 	  free(path);
515 
516 	  return(start_response);
517 	}
518       }else if(ags_regexec(&more_access_regex, path + path_offset, index_max_matches, match_arr, 0) == 0){
519 	AgsExportOutput *export_output;
520 
521 	AgsExportThread *current_export_thread;
522 
523 	path_offset += 3;
524 
525 	if(start_soundcard == NULL){
526 	  osc_response = ags_osc_response_new();
527 	  start_response = g_list_prepend(start_response,
528 					  osc_response);
529 
530 	  ags_osc_response_set_flags(osc_response,
531 				     AGS_OSC_RESPONSE_ERROR);
532 
533 	  g_object_set(osc_response,
534 		       "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_SERVER_FAILURE,
535 		       NULL);
536 
537 	  free(type_tag);
538 	  free(path);
539 
540 	  return(start_response);
541 	}
542 
543 	soundcard = start_soundcard;
544 
545 	while(soundcard != NULL){
546 	  current_export_thread = ags_export_thread_find_soundcard(export_thread,
547 								   soundcard->data);
548 
549 	  export_output = ags_export_output_new(current_export_thread,
550 						soundcard->data,
551 						filename,
552 						tic,
553 						live_performance);
554 
555 	  ags_task_launcher_add_task(task_launcher,
556 				      (AgsTask *) export_output);
557 
558 	  /* create response */
559 	  osc_response = ags_osc_response_new();
560 	  start_response = g_list_prepend(start_response,
561 					  osc_response);
562 
563 	  ags_osc_response_set_flags(osc_response,
564 				     AGS_OSC_RESPONSE_OK);
565 
566 	  soundcard = soundcard->next;
567 	}
568       }else if(ags_regexec(&wildcard_access_regex, path + path_offset, index_max_matches, match_arr, 0) == 0){
569 	AgsExportOutput *export_output;
570 
571 	AgsExportThread *current_export_thread;
572 
573 	path_offset += 3;
574 
575 	if(start_soundcard == NULL){
576 	  osc_response = ags_osc_response_new();
577 	  start_response = g_list_prepend(start_response,
578 					  osc_response);
579 
580 	  ags_osc_response_set_flags(osc_response,
581 				     AGS_OSC_RESPONSE_OK);
582 
583 	  free(type_tag);
584 	  free(path);
585 
586 	  return(start_response);
587 	}
588 
589 	while(soundcard != NULL){
590 	  GList *retval;
591 
592 	  current_export_thread = ags_export_thread_find_soundcard(export_thread,
593 								   soundcard->data);
594 
595 	  export_output = ags_export_output_new(current_export_thread,
596 						soundcard->data,
597 						filename,
598 						tic,
599 						live_performance);
600 
601 	  ags_task_launcher_add_task(task_launcher,
602 				     (AgsTask *) export_output);
603 
604 	  /* create response */
605 	  osc_response = ags_osc_response_new();
606 	  start_response = g_list_prepend(start_response,
607 					  osc_response);
608 
609 	  ags_osc_response_set_flags(osc_response,
610 				     AGS_OSC_RESPONSE_OK);
611 
612 	  soundcard = soundcard->next;
613 	}
614       }else{
615 	osc_response = ags_osc_response_new();
616 	start_response = g_list_prepend(start_response,
617 					osc_response);
618 
619 	ags_osc_response_set_flags(osc_response,
620 				   AGS_OSC_RESPONSE_ERROR);
621 
622 	g_object_set(osc_response,
623 		     "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_SERVER_FAILURE,
624 		     NULL);
625 
626 	g_list_free_full(start_soundcard,
627 			 g_object_unref);
628 
629 	free(type_tag);
630 	free(path);
631 
632 	return(start_response);
633       }
634 
635       g_list_free_full(start_soundcard,
636 		       g_object_unref);
637     }
638   }
639 
640   return(start_response);
641 }
642 
643 /**
644  * ags_osc_export_controller_do_export:
645  * @osc_export_controller: the #AgsOscExportController
646  * @osc_connection: the #AgsOscConnection
647  * @message: the message received
648  * @message_size: the message size
649  *
650  * Run export.
651  *
652  * Returns: the #GList-struct containing #AgsOscResponse
653  *
654  * Since: 3.0.0
655  */
656 gpointer
ags_osc_export_controller_do_export(AgsOscExportController * osc_export_controller,AgsOscConnection * osc_connection,unsigned char * message,guint message_size)657 ags_osc_export_controller_do_export(AgsOscExportController *osc_export_controller,
658 				    AgsOscConnection *osc_connection,
659 				    unsigned char *message, guint message_size)
660 {
661   gpointer osc_response;
662 
663   g_return_val_if_fail(AGS_IS_OSC_EXPORT_CONTROLLER(osc_export_controller), NULL);
664 
665   g_object_ref((GObject *) osc_export_controller);
666   g_signal_emit(G_OBJECT(osc_export_controller),
667 		osc_export_controller_signals[DO_EXPORT], 0,
668 		osc_connection,
669 		message, message_size,
670 		&osc_response);
671   g_object_unref((GObject *) osc_export_controller);
672 
673   return(osc_response);
674 }
675 
676 /**
677  * ags_osc_export_controller_new:
678  *
679  * Instantiate new #AgsOscExportController
680  *
681  * Returns: the #AgsOscExportController
682  *
683  * Since: 3.0.0
684  */
685 AgsOscExportController*
ags_osc_export_controller_new()686 ags_osc_export_controller_new()
687 {
688   AgsOscExportController *osc_export_controller;
689 
690   osc_export_controller = (AgsOscExportController *) g_object_new(AGS_TYPE_OSC_EXPORT_CONTROLLER,
691 								  NULL);
692 
693   return(osc_export_controller);
694 }
695