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(®ex_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(®ex_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