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