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_action_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_start_audio.h>
26 #include <ags/audio/task/ags_cancel_audio.h>
27 #include <ags/audio/task/ags_start_soundcard.h>
28 #include <ags/audio/task/ags_stop_soundcard.h>
29 #include <ags/audio/task/ags_start_sequencer.h>
30 #include <ags/audio/task/ags_stop_sequencer.h>
31 
32 #include <ags/audio/osc/ags_osc_response.h>
33 #include <ags/audio/osc/ags_osc_buffer_util.h>
34 
35 #include <ags/i18n.h>
36 
37 #include <stdlib.h>
38 
39 void ags_osc_action_controller_class_init(AgsOscActionControllerClass *osc_action_controller);
40 void ags_osc_action_controller_init(AgsOscActionController *osc_action_controller);
41 void ags_osc_action_controller_set_property(GObject *gobject,
42 					    guint prop_id,
43 					    const GValue *value,
44 					    GParamSpec *param_spec);
45 void ags_osc_action_controller_get_property(GObject *gobject,
46 					    guint prop_id,
47 					    GValue *value,
48 					    GParamSpec *param_spec);
49 void ags_osc_action_controller_dispose(GObject *gobject);
50 void ags_osc_action_controller_finalize(GObject *gobject);
51 
52 gpointer ags_osc_action_controller_real_run_action(AgsOscActionController *osc_action_controller,
53 						   AgsOscConnection *osc_connection,
54 						   unsigned char *message, guint message_size);
55 
56 /**
57  * SECTION:ags_osc_action_controller
58  * @short_description: OSC action controller
59  * @title: AgsOscActionController
60  * @section_id:
61  * @include: ags/audio/osc/controller/ags_osc_action_controller.h
62  *
63  * The #AgsOscActionController implements the OSC action controller.
64  */
65 
66 enum{
67   PROP_0,
68 };
69 
70 enum{
71   RUN_ACTION,
72   LAST_SIGNAL,
73 };
74 
75 static gpointer ags_osc_action_controller_parent_class = NULL;
76 static guint osc_action_controller_signals[LAST_SIGNAL];
77 
78 GType
ags_osc_action_controller_get_type()79 ags_osc_action_controller_get_type()
80 {
81   static volatile gsize g_define_type_id__volatile = 0;
82 
83   if(g_once_init_enter (&g_define_type_id__volatile)){
84     GType ags_type_osc_action_controller = 0;
85 
86     static const GTypeInfo ags_osc_action_controller_info = {
87       sizeof (AgsOscActionControllerClass),
88       NULL, /* base_init */
89       NULL, /* base_finalize */
90       (GClassInitFunc) ags_osc_action_controller_class_init,
91       NULL, /* class_finalize */
92       NULL, /* class_data */
93       sizeof (AgsOscActionController),
94       0,    /* n_preallocs */
95       (GInstanceInitFunc) ags_osc_action_controller_init,
96     };
97 
98     ags_type_osc_action_controller = g_type_register_static(AGS_TYPE_OSC_CONTROLLER,
99 							    "AgsOscActionController",
100 							    &ags_osc_action_controller_info,
101 							    0);
102 
103     g_once_init_leave(&g_define_type_id__volatile, ags_type_osc_action_controller);
104   }
105 
106   return g_define_type_id__volatile;
107 }
108 
109 void
ags_osc_action_controller_class_init(AgsOscActionControllerClass * osc_action_controller)110 ags_osc_action_controller_class_init(AgsOscActionControllerClass *osc_action_controller)
111 {
112   GObjectClass *gobject;
113   GParamSpec *param_spec;
114 
115   ags_osc_action_controller_parent_class = g_type_class_peek_parent(osc_action_controller);
116 
117   /* GObjectClass */
118   gobject = (GObjectClass *) osc_action_controller;
119 
120   gobject->set_property = ags_osc_action_controller_set_property;
121   gobject->get_property = ags_osc_action_controller_get_property;
122 
123   gobject->dispose = ags_osc_action_controller_dispose;
124   gobject->finalize = ags_osc_action_controller_finalize;
125 
126   /* properties */
127 
128   /* AgsOscActionControllerClass */
129   osc_action_controller->run_action = ags_osc_action_controller_real_run_action;
130 
131   /* signals */
132   /**
133    * AgsOscActionController::run-action:
134    * @osc_action_controller: the #AgsOscActionController
135    * @osc_connection: the #AgsOscConnection
136    * @message: the message received
137    * @message_size: the message size
138    *
139    * The ::run-action signal is emited during get data of action controller.
140    *
141    * Returns: the #AgsOscResponse
142    *
143    * Since: 3.0.0
144    */
145   osc_action_controller_signals[RUN_ACTION] =
146     g_signal_new("run-action",
147 		 G_TYPE_FROM_CLASS(osc_action_controller),
148 		 G_SIGNAL_RUN_LAST,
149 		 G_STRUCT_OFFSET(AgsOscActionControllerClass, run_action),
150 		 NULL, NULL,
151 		 ags_cclosure_marshal_POINTER__OBJECT_POINTER_UINT,
152 		 G_TYPE_POINTER, 3,
153 		 G_TYPE_OBJECT,
154 		 G_TYPE_POINTER,
155 		 G_TYPE_UINT);
156 }
157 
158 void
ags_osc_action_controller_init(AgsOscActionController * osc_action_controller)159 ags_osc_action_controller_init(AgsOscActionController *osc_action_controller)
160 {
161   g_object_set(osc_action_controller,
162 	       "context-path", "/action",
163 	       NULL);
164 }
165 
166 void
ags_osc_action_controller_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)167 ags_osc_action_controller_set_property(GObject *gobject,
168 				       guint prop_id,
169 				       const GValue *value,
170 				       GParamSpec *param_spec)
171 {
172   AgsOscActionController *osc_action_controller;
173 
174   GRecMutex *osc_controller_mutex;
175 
176   osc_action_controller = AGS_OSC_ACTION_CONTROLLER(gobject);
177 
178   /* get osc controller mutex */
179   osc_controller_mutex = AGS_OSC_CONTROLLER_GET_OBJ_MUTEX(osc_action_controller);
180 
181   switch(prop_id){
182   default:
183     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
184     break;
185   }
186 }
187 
188 void
ags_osc_action_controller_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)189 ags_osc_action_controller_get_property(GObject *gobject,
190 				       guint prop_id,
191 				       GValue *value,
192 				       GParamSpec *param_spec)
193 {
194   AgsOscActionController *osc_action_controller;
195 
196   GRecMutex *osc_controller_mutex;
197 
198   osc_action_controller = AGS_OSC_ACTION_CONTROLLER(gobject);
199 
200   /* get osc controller mutex */
201   osc_controller_mutex = AGS_OSC_CONTROLLER_GET_OBJ_MUTEX(osc_action_controller);
202 
203   switch(prop_id){
204   default:
205     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
206     break;
207   }
208 }
209 
210 void
ags_osc_action_controller_dispose(GObject * gobject)211 ags_osc_action_controller_dispose(GObject *gobject)
212 {
213   AgsOscActionController *osc_action_controller;
214 
215   osc_action_controller = AGS_OSC_ACTION_CONTROLLER(gobject);
216 
217   /* call parent */
218   G_OBJECT_CLASS(ags_osc_action_controller_parent_class)->dispose(gobject);
219 }
220 
221 void
ags_osc_action_controller_finalize(GObject * gobject)222 ags_osc_action_controller_finalize(GObject *gobject)
223 {
224   AgsOscActionController *osc_action_controller;
225 
226   osc_action_controller = AGS_OSC_ACTION_CONTROLLER(gobject);
227 
228   /* call parent */
229   G_OBJECT_CLASS(ags_osc_action_controller_parent_class)->finalize(gobject);
230 }
231 
232 gpointer
ags_osc_action_controller_real_run_action(AgsOscActionController * osc_action_controller,AgsOscConnection * osc_connection,unsigned char * message,guint message_size)233 ags_osc_action_controller_real_run_action(AgsOscActionController *osc_action_controller,
234 					  AgsOscConnection *osc_connection,
235 					  unsigned char *message, guint message_size)
236 {
237   AgsOscResponse *osc_response;
238 
239   AgsTaskLauncher *task_launcher;
240   AgsTask *task;
241 
242   AgsApplicationContext *application_context;
243 
244   GList *start_response;
245   GList *start_list, *list;
246 
247   gchar *type_tag;
248   gchar *path;
249   gchar *action;
250 
251   guint length;
252   guint path_offset;
253   gboolean success;
254 
255   start_response = NULL;
256 
257   /* read type tag */
258   ags_osc_buffer_util_get_string(message + 8,
259 				 &type_tag, NULL);
260 
261   success = (type_tag != NULL &&
262 	     !strncmp(type_tag, ",ss", 4)) ? TRUE: FALSE;
263 
264   if(!success){
265     osc_response = ags_osc_response_new();
266     start_response = g_list_prepend(start_response,
267 				    osc_response);
268 
269     ags_osc_response_set_flags(osc_response,
270 			       AGS_OSC_RESPONSE_ERROR);
271 
272     g_object_set(osc_response,
273 		 "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MALFORMED_REQUEST,
274 		 NULL);
275 
276     if(type_tag != NULL){
277       free(type_tag);
278     }
279 
280     return(start_response);
281   }
282 
283   /* read path */
284   ags_osc_buffer_util_get_string(message + 12,
285 				 &path, NULL);
286 
287   /* check argument */
288   if(path == NULL){
289     osc_response = ags_osc_response_new();
290     start_response = g_list_prepend(start_response,
291 				    osc_response);
292 
293     ags_osc_response_set_flags(osc_response,
294 			       AGS_OSC_RESPONSE_ERROR);
295 
296     g_object_set(osc_response,
297 		 "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MALFORMED_REQUEST,
298 		 NULL);
299 
300     free(type_tag);
301 
302     return(start_response);
303   }
304 
305   length = strlen(path);
306 
307   /* read action */
308   ags_osc_buffer_util_get_string(message + 12 + (4 * (guint) ceil((double) (length + 1) / 4.0)),
309 				 &action, NULL);
310 
311   /* check argument */
312   if(action == NULL){
313     osc_response = ags_osc_response_new();
314     start_response = g_list_prepend(start_response,
315 				    osc_response);
316 
317     ags_osc_response_set_flags(osc_response,
318 			       AGS_OSC_RESPONSE_ERROR);
319 
320     g_object_set(osc_response,
321 		 "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MALFORMED_REQUEST,
322 		 NULL);
323 
324     free(type_tag);
325     free(path);
326 
327     return(start_response);
328   }
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       path_offset += 13;
342 
343       if(!g_strcmp0("start",
344 		    action)){
345 	task = (AgsTask *) ags_start_soundcard_new(application_context);
346 
347 	ags_task_launcher_add_task(task_launcher,
348 				   task);
349       }else if(!g_strcmp0("stop",
350 			  action)){
351 	task = (AgsTask *) ags_stop_soundcard_new(application_context);
352 
353 	ags_task_launcher_add_task(task_launcher,
354 				   task);
355       }else{
356 	osc_response = ags_osc_response_new();
357 	start_response = g_list_prepend(start_response,
358 					osc_response);
359 
360 	ags_osc_response_set_flags(osc_response,
361 				   AGS_OSC_RESPONSE_ERROR);
362 
363 	g_object_set(osc_response,
364 		     "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_UNKNOWN_ARGUMENT,
365 		     NULL);
366 
367 	free(type_tag);
368 	free(path);
369 	free(action);
370 
371 	g_object_unref(task_launcher);
372 
373 	return(start_response);
374       }
375     }else if(!strncmp(path + path_offset, "/AgsSequencer", 13)){
376       path_offset += 13;
377 
378       if(!g_strcmp0("start",
379 		    action)){
380 	task = (AgsTask *) ags_start_sequencer_new(application_context);
381 
382 	ags_task_launcher_add_task(task_launcher,
383 				   task);
384       }else if(!g_strcmp0("stop",
385 			  action)){
386 	task = (AgsTask *) ags_stop_sequencer_new(application_context);
387 
388 	ags_task_launcher_add_task(task_launcher,
389 				   task);
390       }else{
391 	osc_response = ags_osc_response_new();
392 	start_response = g_list_prepend(start_response,
393 					osc_response);
394 
395 	ags_osc_response_set_flags(osc_response,
396 				   AGS_OSC_RESPONSE_ERROR);
397 
398 	g_object_set(osc_response,
399 		     "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_UNKNOWN_ARGUMENT,
400 		     NULL);
401 
402 	free(type_tag);
403 	free(path);
404 	free(action);
405 
406 	g_object_unref(task_launcher);
407 
408 	return(start_response);
409       }
410     }else if(!strncmp(path + path_offset, "/AgsAudio", 9)){
411       AgsAudio *audio;
412 
413       guint nth_audio;
414       int retval;
415 
416       audio = NULL;
417 
418       path_offset += 9;
419 
420       if(g_ascii_isdigit(path[path_offset + 1])){
421 	start_list = ags_sound_provider_get_audio(AGS_SOUND_PROVIDER(application_context));
422 
423 	retval = sscanf(path + path_offset, "[%d]",
424 			&nth_audio);
425 
426 	if(retval <= 0){
427 	  osc_response = ags_osc_response_new();
428 	  start_response = g_list_prepend(start_response,
429 					  osc_response);
430 
431 	  ags_osc_response_set_flags(osc_response,
432 				     AGS_OSC_RESPONSE_ERROR);
433 
434 	  g_object_set(osc_response,
435 		       "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MISSING_INDEX,
436 		       NULL);
437 
438 	  free(type_tag);
439 	  free(path);
440 	  free(action);
441 
442 	  g_object_unref(task_launcher);
443 	  g_list_free_full(start_list,
444 			   g_object_unref);
445 
446 	  return(start_response);
447 	}
448 
449 	audio = g_list_nth_data(start_list,
450 				nth_audio);
451 
452 	path_offset = strchr(path + path_offset, ']') - path + 1;
453 
454 	g_list_free_full(start_list,
455 			 g_object_unref);
456       }else if(path[path_offset + 1] == '"'){
457 	gchar *audio_name;
458 	gchar *offset;
459 
460 	guint length;
461 
462 	if((offset = strchr(path + path_offset + 2, '"')) == NULL){
463 	  osc_response = ags_osc_response_new();
464 	  start_response = g_list_prepend(start_response,
465 					  osc_response);
466 
467 	  ags_osc_response_set_flags(osc_response,
468 				     AGS_OSC_RESPONSE_ERROR);
469 
470 	  g_object_set(osc_response,
471 		       "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_CHUNK_SIZE_EXCEEDED,
472 		       NULL);
473 
474 	  free(type_tag);
475 	  free(path);
476 	  free(action);
477 
478 	  g_object_unref(task_launcher);
479 
480 	  return(start_response);
481 	}
482 
483 	length = offset - (path + path_offset + 3);
484 
485 	audio_name = malloc((length + 1) * sizeof(gchar));
486 	sscanf(path + path_offset, "%ms", &audio_name);
487 
488 	start_list = ags_sound_provider_get_audio(AGS_SOUND_PROVIDER(application_context));
489 
490 	list = ags_audio_find_name(start_list,
491 				   audio_name);
492 
493 	if(list != NULL){
494 	  audio = list->data;
495 	}
496 
497 	path_offset = path_offset + strlen(audio_name) + 2;
498 
499 	g_list_free_full(start_list,
500 			 g_object_unref);
501       }else{
502 	osc_response = ags_osc_response_new();
503 	start_response = g_list_prepend(start_response,
504 					osc_response);
505 
506 	ags_osc_response_set_flags(osc_response,
507 				   AGS_OSC_RESPONSE_ERROR);
508 
509 	g_object_set(osc_response,
510 		     "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_MISSING_INDEX,
511 		     NULL);
512 
513 	free(type_tag);
514 	free(path);
515 	free(action);
516 
517 	g_object_unref(task_launcher);
518 
519 	return(start_response);
520       }
521 
522       if(!g_strcmp0("start",
523 		    action)){
524 	task = (AgsTask *) ags_start_audio_new(audio,
525 					       -1);
526 
527 	ags_task_launcher_add_task(task_launcher,
528 				   task);
529       }else if(!g_strcmp0("stop",
530 			  action)){
531 	task = (AgsTask *) ags_cancel_audio_new(audio,
532 						-1);
533 
534 	ags_task_launcher_add_task(task_launcher,
535 				   task);
536       }else{
537 	osc_response = ags_osc_response_new();
538 	start_response = g_list_prepend(start_response,
539 					osc_response);
540 
541 	ags_osc_response_set_flags(osc_response,
542 				   AGS_OSC_RESPONSE_ERROR);
543 
544 	g_object_set(osc_response,
545 		     "error-message", AGS_OSC_RESPONSE_ERROR_MESSAGE_UNKNOWN_ARGUMENT,
546 		     NULL);
547 
548 	free(type_tag);
549 	free(path);
550 	free(action);
551 
552 	g_object_unref(task_launcher);
553 
554 	return(start_response);
555       }
556     }
557   }
558 
559   /* create response */
560   osc_response = ags_osc_response_new();
561   start_response = g_list_prepend(start_response,
562 				  osc_response);
563 
564   ags_osc_response_set_flags(osc_response,
565 			     AGS_OSC_RESPONSE_OK);
566 
567   free(type_tag);
568   free(path);
569   free(action);
570 
571   g_object_unref(task_launcher);
572 
573   return(start_response);
574 }
575 
576 /**
577  * ags_osc_action_controller_run_action:
578  * @osc_action_controller: the #AgsOscActionController
579  * @osc_connection: the #AgsOscConnection
580  * @message: the message received
581  * @message_size: the message size
582  *
583  * Run action.
584  *
585  * Returns: the #GList-struct containing #AgsOscResponse
586  *
587  * Since: 3.0.0
588  */
589 gpointer
ags_osc_action_controller_run_action(AgsOscActionController * osc_action_controller,AgsOscConnection * osc_connection,unsigned char * message,guint message_size)590 ags_osc_action_controller_run_action(AgsOscActionController *osc_action_controller,
591 				     AgsOscConnection *osc_connection,
592 				     unsigned char *message, guint message_size)
593 {
594   gpointer osc_response;
595 
596   g_return_val_if_fail(AGS_IS_OSC_ACTION_CONTROLLER(osc_action_controller), NULL);
597 
598   g_object_ref((GObject *) osc_action_controller);
599   g_signal_emit(G_OBJECT(osc_action_controller),
600 		osc_action_controller_signals[RUN_ACTION], 0,
601 		osc_connection,
602 		message, message_size,
603 		&osc_response);
604   g_object_unref((GObject *) osc_action_controller);
605 
606   return(osc_response);
607 }
608 
609 /**
610  * ags_osc_action_controller_new:
611  *
612  * Instantiate new #AgsOscActionController
613  *
614  * Returns: the #AgsOscActionController
615  *
616  * Since: 3.0.0
617  */
618 AgsOscActionController*
ags_osc_action_controller_new()619 ags_osc_action_controller_new()
620 {
621   AgsOscActionController *osc_action_controller;
622 
623   osc_action_controller = (AgsOscActionController *) g_object_new(AGS_TYPE_OSC_ACTION_CONTROLLER,
624 								  NULL);
625 
626   return(osc_action_controller);
627 }
628