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/X/ags_input_collection_editor.h>
21 #include <ags/X/ags_input_collection_editor_callbacks.h>
22
23 #include <ags/X/ags_window.h>
24 #include <ags/X/ags_machine.h>
25 #include <ags/X/ags_connection_editor.h>
26
27 #include <ags/i18n.h>
28
29 void ags_input_collection_editor_class_init(AgsInputCollectionEditorClass *input_collection_editor);
30 void ags_input_collection_editor_connectable_interface_init(AgsConnectableInterface *connectable);
31 void ags_input_collection_editor_applicable_interface_init(AgsApplicableInterface *applicable);
32 void ags_input_collection_editor_init(AgsInputCollectionEditor *input_collection_editor);
33 void ags_input_collection_editor_set_property(GObject *gobject,
34 guint prop_id,
35 const GValue *value,
36 GParamSpec *param_spec);
37 void ags_input_collection_editor_get_property(GObject *gobject,
38 guint prop_id,
39 GValue *value,
40 GParamSpec *param_spec);
41
42 void ags_input_collection_editor_connect(AgsConnectable *connectable);
43 void ags_input_collection_editor_disconnect(AgsConnectable *connectable);
44
45 void ags_input_collection_editor_set_update(AgsApplicable *applicable, gboolean update);
46 void ags_input_collection_editor_apply(AgsApplicable *applicable);
47 void ags_input_collection_editor_reset(AgsApplicable *applicable);
48
49 /**
50 * SECTION:ags_input_collection_editor
51 * @short_description: edit audio connections in bulk mode.
52 * @title: AgsInputCollectionEditor
53 * @section_id:
54 * @include: ags/X/ags_input_collection_editor.h
55 *
56 * #AgsInputCollectionEditor is a composite widget to modify audio connections. A input collection
57 * editor should be packed by a #AgsConnectionEditor.
58 */
59
60 static gpointer ags_input_collection_editor_parent_class = NULL;
61
62 enum{
63 PROP_0,
64 PROP_CHANNEL_TYPE,
65 };
66
67 GType
ags_input_collection_editor_get_type(void)68 ags_input_collection_editor_get_type(void)
69 {
70 static volatile gsize g_define_type_id__volatile = 0;
71
72 if(g_once_init_enter (&g_define_type_id__volatile)){
73 GType ags_type_input_collection_editor = 0;
74
75 static const GTypeInfo ags_input_collection_editor_info = {
76 sizeof (AgsInputCollectionEditorClass),
77 NULL, /* base_init */
78 NULL, /* base_finalize */
79 (GClassInitFunc) ags_input_collection_editor_class_init,
80 NULL, /* class_finalize */
81 NULL, /* class_data */
82 sizeof (AgsInputCollectionEditor),
83 0, /* n_preallocs */
84 (GInstanceInitFunc) ags_input_collection_editor_init,
85 };
86
87 static const GInterfaceInfo ags_connectable_interface_info = {
88 (GInterfaceInitFunc) ags_input_collection_editor_connectable_interface_init,
89 NULL, /* interface_finalize */
90 NULL, /* interface_data */
91 };
92
93 static const GInterfaceInfo ags_applicable_interface_info = {
94 (GInterfaceInitFunc) ags_input_collection_editor_applicable_interface_init,
95 NULL, /* interface_finalize */
96 NULL, /* interface_data */
97 };
98
99 ags_type_input_collection_editor = g_type_register_static(GTK_TYPE_GRID,
100 "AgsInputCollectionEditor",
101 &ags_input_collection_editor_info,
102 0);
103
104 g_type_add_interface_static(ags_type_input_collection_editor,
105 AGS_TYPE_CONNECTABLE,
106 &ags_connectable_interface_info);
107
108 g_type_add_interface_static(ags_type_input_collection_editor,
109 AGS_TYPE_APPLICABLE,
110 &ags_applicable_interface_info);
111
112 g_once_init_leave(&g_define_type_id__volatile, ags_type_input_collection_editor);
113 }
114
115 return g_define_type_id__volatile;
116 }
117
118 void
ags_input_collection_editor_class_init(AgsInputCollectionEditorClass * input_collection_editor)119 ags_input_collection_editor_class_init(AgsInputCollectionEditorClass *input_collection_editor)
120 {
121 GObjectClass *gobject;
122 GParamSpec *param_spec;
123
124 ags_input_collection_editor_parent_class = g_type_class_peek_parent(input_collection_editor);
125
126 /* GObjectClass */
127 gobject = (GObjectClass *) input_collection_editor;
128
129 gobject->set_property = ags_input_collection_editor_set_property;
130 gobject->get_property = ags_input_collection_editor_get_property;
131
132 /* properties */
133 /**
134 * AgsInputCollectionEditor:channel-type:
135 *
136 * The channel type to apply to. Either %AGS_TYPE_INPUT or %AGS_TYPE_OUTPUT.
137 *
138 * Since: 3.0.0
139 */
140 param_spec = g_param_spec_gtype("channel-type",
141 i18n_pspec("assigned channel type"),
142 i18n_pspec("The channel type which this channel input collection editor is assigned with"),
143 G_TYPE_NONE,
144 G_PARAM_READABLE | G_PARAM_WRITABLE);
145 g_object_class_install_property(gobject,
146 PROP_CHANNEL_TYPE,
147 param_spec);
148 }
149
150 void
ags_input_collection_editor_connectable_interface_init(AgsConnectableInterface * connectable)151 ags_input_collection_editor_connectable_interface_init(AgsConnectableInterface *connectable)
152 {
153 connectable->connect = ags_input_collection_editor_connect;
154 connectable->disconnect = ags_input_collection_editor_disconnect;
155 }
156
157 void
ags_input_collection_editor_applicable_interface_init(AgsApplicableInterface * applicable)158 ags_input_collection_editor_applicable_interface_init(AgsApplicableInterface *applicable)
159 {
160 applicable->set_update = ags_input_collection_editor_set_update;
161 applicable->apply = ags_input_collection_editor_apply;
162 applicable->reset = ags_input_collection_editor_reset;
163 }
164
165 void
ags_input_collection_editor_init(AgsInputCollectionEditor * input_collection_editor)166 ags_input_collection_editor_init(AgsInputCollectionEditor *input_collection_editor)
167 {
168 GtkLabel *label;
169
170 input_collection_editor->flags = 0;
171
172 g_signal_connect_after(GTK_WIDGET(input_collection_editor), "parent-set",
173 G_CALLBACK(ags_input_collection_editor_parent_set_callback), input_collection_editor);
174
175 input_collection_editor->channel_type = G_TYPE_NONE;
176
177 /* first line */
178 label = (GtkLabel *) gtk_label_new(i18n("first line"));
179
180 gtk_label_set_yalign(label,
181 0.5);
182
183 gtk_widget_set_halign((GtkWidget *) label,
184 GTK_ALIGN_FILL);
185 gtk_widget_set_valign((GtkWidget *) label,
186 GTK_ALIGN_FILL);
187 gtk_widget_set_hexpand((GtkWidget *) label,
188 TRUE);
189 gtk_widget_set_vexpand((GtkWidget *) label,
190 TRUE);
191
192 gtk_grid_attach((GtkGrid *) input_collection_editor,
193 (GtkWidget *) label,
194 0, 0,
195 1, 1);
196
197 input_collection_editor->first_line = (GtkSpinButton *) gtk_spin_button_new_with_range(-1.0,
198 -1.0,
199 1.0);
200
201 gtk_widget_set_halign((GtkWidget *) input_collection_editor->first_line,
202 GTK_ALIGN_FILL);
203 gtk_widget_set_valign((GtkWidget *) input_collection_editor->first_line,
204 GTK_ALIGN_FILL);
205 gtk_widget_set_hexpand((GtkWidget *) input_collection_editor->first_line,
206 TRUE);
207 gtk_widget_set_vexpand((GtkWidget *) input_collection_editor->first_line,
208 TRUE);
209
210 gtk_grid_attach((GtkGrid *) input_collection_editor,
211 (GtkWidget *) input_collection_editor->first_line,
212 1, 0,
213 1, 1);
214
215 /* count */
216 label = (GtkLabel *) gtk_label_new(i18n("count"));
217
218 gtk_label_set_yalign(label,
219 0.5);
220
221 gtk_widget_set_halign((GtkWidget *) label,
222 GTK_ALIGN_FILL);
223 gtk_widget_set_valign((GtkWidget *) label,
224 GTK_ALIGN_FILL);
225 gtk_widget_set_hexpand((GtkWidget *) label,
226 TRUE);
227 gtk_widget_set_vexpand((GtkWidget *) label,
228 TRUE);
229
230 gtk_grid_attach((GtkGrid *) input_collection_editor,
231 (GtkWidget *) label,
232 0, 1,
233 1, 1);
234
235 input_collection_editor->count = (GtkSpinButton *) gtk_spin_button_new_with_range(-1.0,
236 -1.0,
237 1.0);
238
239 gtk_widget_set_halign((GtkWidget *) input_collection_editor->count,
240 GTK_ALIGN_FILL);
241 gtk_widget_set_valign((GtkWidget *) input_collection_editor->count,
242 GTK_ALIGN_FILL);
243 gtk_widget_set_hexpand((GtkWidget *) input_collection_editor->count,
244 TRUE);
245 gtk_widget_set_vexpand((GtkWidget *) input_collection_editor->count,
246 TRUE);
247
248 gtk_grid_attach((GtkGrid *) input_collection_editor,
249 GTK_WIDGET(input_collection_editor->count),
250 1, 1,
251 1, 1);
252
253 /* soundcard */
254 label = (GtkLabel *) gtk_label_new(i18n("soundcard"));
255
256 gtk_label_set_yalign(label,
257 0.5);
258
259 gtk_widget_set_halign((GtkWidget *) label,
260 GTK_ALIGN_FILL);
261 gtk_widget_set_valign((GtkWidget *) label,
262 GTK_ALIGN_FILL);
263 gtk_widget_set_hexpand((GtkWidget *) label,
264 TRUE);
265 gtk_widget_set_vexpand((GtkWidget *) label,
266 TRUE);
267
268 gtk_grid_attach((GtkGrid *) input_collection_editor,
269 (GtkWidget *) label,
270 0, 2,
271 1, 1);
272
273 input_collection_editor->soundcard = (GtkComboBox *) gtk_combo_box_text_new();
274
275 gtk_widget_set_halign((GtkWidget *) input_collection_editor->soundcard,
276 GTK_ALIGN_FILL);
277 gtk_widget_set_valign((GtkWidget *) input_collection_editor->soundcard,
278 GTK_ALIGN_FILL);
279 gtk_widget_set_hexpand((GtkWidget *) input_collection_editor->soundcard,
280 TRUE);
281 gtk_widget_set_vexpand((GtkWidget *) input_collection_editor->soundcard,
282 TRUE);
283
284 gtk_grid_attach((GtkGrid *) input_collection_editor,
285 (GtkWidget *) input_collection_editor->soundcard,
286 1, 2,
287 1, 1);
288
289 /* audio channel */
290 label = (GtkLabel *) gtk_label_new(i18n("audio channel"));
291
292 gtk_label_set_yalign(label,
293 0.5);
294
295 gtk_widget_set_halign((GtkWidget *) label,
296 GTK_ALIGN_FILL);
297 gtk_widget_set_valign((GtkWidget *) label,
298 GTK_ALIGN_FILL);
299 gtk_widget_set_hexpand((GtkWidget *) label,
300 TRUE);
301 gtk_widget_set_vexpand((GtkWidget *) label,
302 TRUE);
303
304 gtk_grid_attach((GtkGrid *) input_collection_editor,
305 (GtkWidget *) label,
306 0, 3,
307 1, 1);
308
309 input_collection_editor->audio_channel = (GtkSpinButton *) gtk_spin_button_new_with_range(-1.0,
310 -1.0,
311 1.0);
312
313 gtk_widget_set_halign((GtkWidget *) input_collection_editor->audio_channel,
314 GTK_ALIGN_FILL);
315 gtk_widget_set_valign((GtkWidget *) input_collection_editor->audio_channel,
316 GTK_ALIGN_FILL);
317 gtk_widget_set_hexpand((GtkWidget *) input_collection_editor->audio_channel,
318 TRUE);
319 gtk_widget_set_vexpand((GtkWidget *) input_collection_editor->audio_channel,
320 TRUE);
321
322 gtk_grid_attach((GtkGrid *) input_collection_editor,
323 (GtkWidget *) input_collection_editor->audio_channel,
324 1, 3,
325 1, 1);
326 }
327
328 void
ags_input_collection_editor_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)329 ags_input_collection_editor_set_property(GObject *gobject,
330 guint prop_id,
331 const GValue *value,
332 GParamSpec *param_spec)
333 {
334 AgsInputCollectionEditor *input_collection_editor;
335
336 input_collection_editor = AGS_INPUT_COLLECTION_EDITOR(gobject);
337
338 switch(prop_id){
339 case PROP_CHANNEL_TYPE:
340 {
341 input_collection_editor->channel_type = g_value_get_gtype(value);
342 }
343 break;
344 default:
345 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
346 break;
347 }
348 }
349
350 void
ags_input_collection_editor_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)351 ags_input_collection_editor_get_property(GObject *gobject,
352 guint prop_id,
353 GValue *value,
354 GParamSpec *param_spec)
355 {
356 AgsInputCollectionEditor *input_collection_editor;
357
358 input_collection_editor = AGS_INPUT_COLLECTION_EDITOR(gobject);
359
360 switch(prop_id){
361 case PROP_CHANNEL_TYPE:
362 {
363 g_value_set_gtype(value, input_collection_editor->channel_type);
364 }
365 break;
366 default:
367 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
368 break;
369 }
370 }
371
372 void
ags_input_collection_editor_connect(AgsConnectable * connectable)373 ags_input_collection_editor_connect(AgsConnectable *connectable)
374 {
375 AgsInputCollectionEditor *input_collection_editor;
376
377 /* AgsInputCollectionEditor */
378 input_collection_editor = AGS_INPUT_COLLECTION_EDITOR(connectable);
379
380 if((AGS_INPUT_COLLECTION_EDITOR_CONNECTED & (input_collection_editor->flags)) != 0){
381 return;
382 }
383
384 input_collection_editor->flags |= AGS_INPUT_COLLECTION_EDITOR_CONNECTED;
385
386 g_signal_connect_after(G_OBJECT(input_collection_editor->soundcard), "changed",
387 G_CALLBACK(ags_input_collection_editor_soundcard_callback), input_collection_editor);
388 }
389
390 void
ags_input_collection_editor_disconnect(AgsConnectable * connectable)391 ags_input_collection_editor_disconnect(AgsConnectable *connectable)
392 {
393 AgsInputCollectionEditor *input_collection_editor;
394
395 /* AgsInputCollectionEditor */
396 input_collection_editor = AGS_INPUT_COLLECTION_EDITOR(connectable);
397
398 if((AGS_INPUT_COLLECTION_EDITOR_CONNECTED & (input_collection_editor->flags)) == 0){
399 return;
400 }
401
402 input_collection_editor->flags &= (~AGS_INPUT_COLLECTION_EDITOR_CONNECTED);
403
404 g_object_disconnect(G_OBJECT(input_collection_editor->soundcard),
405 "any_signal::changed",
406 G_CALLBACK(ags_input_collection_editor_soundcard_callback),
407 input_collection_editor,
408 NULL);
409 }
410
411 void
ags_input_collection_editor_set_update(AgsApplicable * applicable,gboolean update)412 ags_input_collection_editor_set_update(AgsApplicable *applicable, gboolean update)
413 {
414 /* empty */
415 }
416
417 void
ags_input_collection_editor_apply(AgsApplicable * applicable)418 ags_input_collection_editor_apply(AgsApplicable *applicable)
419 {
420 AgsInputCollectionEditor *input_collection_editor;
421 GtkTreeIter iter;
422
423 input_collection_editor = AGS_INPUT_COLLECTION_EDITOR(applicable);
424
425 if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(input_collection_editor->soundcard),
426 &iter)){
427 AgsMachine *machine;
428 AgsConnectionEditor *connection_editor;
429
430 AgsAudio *audio;
431 AgsChannel *start_output, *start_input;
432 AgsChannel *channel, *nth_channel;
433
434 GObject *input_soundcard;
435
436 GtkTreeModel *model;
437
438 guint first_line, count;
439 guint audio_channel;
440 guint i;
441
442 connection_editor = AGS_CONNECTION_EDITOR(gtk_widget_get_ancestor(GTK_WIDGET(input_collection_editor),
443 AGS_TYPE_CONNECTION_EDITOR));
444 machine = connection_editor->machine;
445 audio = machine->audio;
446
447 /* get mapping and soundcard */
448 first_line = (guint) gtk_spin_button_get_value_as_int(input_collection_editor->first_line);
449 count = (guint) gtk_spin_button_get_value_as_int(input_collection_editor->count);
450
451 audio_channel = (guint) gtk_spin_button_get_value_as_int(input_collection_editor->audio_channel);
452
453 model = gtk_combo_box_get_model(GTK_COMBO_BOX(input_collection_editor->soundcard));
454 gtk_tree_model_get(model,
455 &iter,
456 1, &input_soundcard,
457 -1);
458
459 /* apply */
460 g_object_get(audio,
461 "output", &start_output,
462 "input", &start_input,
463 NULL);
464
465 for(i = 0; i < count; i++){
466 channel = NULL;
467
468 if(g_type_is_a(input_collection_editor->channel_type, AGS_TYPE_OUTPUT)){
469 channel = start_output;
470 }else if(g_type_is_a(input_collection_editor->channel_type, AGS_TYPE_INPUT)){
471 channel = start_input;
472 }
473
474 nth_channel = ags_channel_nth(channel,
475 first_line + i);
476
477 g_object_set(nth_channel,
478 "input-soundcard", input_soundcard,
479 "input-soundcard-channel", audio_channel,
480 NULL);
481
482 g_object_unref(nth_channel);
483 }
484
485 if(start_output != NULL){
486 g_object_unref(start_output);
487 }
488
489 if(start_input != NULL){
490 g_object_unref(start_input);
491 }
492 }
493 }
494
495 void
ags_input_collection_editor_reset(AgsApplicable * applicable)496 ags_input_collection_editor_reset(AgsApplicable *applicable)
497 {
498 /* empty */
499 }
500
501 /**
502 * ags_input_collection_editor_check:
503 * @input_collection_editor: the #AgsInputCollectionEditor
504 *
505 * Checks for possible channels to input. And modifies its ranges.
506 *
507 * Since: 3.0.0
508 */
509 void
ags_input_collection_editor_check(AgsInputCollectionEditor * input_collection_editor)510 ags_input_collection_editor_check(AgsInputCollectionEditor *input_collection_editor)
511 {
512 AgsConnectionEditor *connection_editor;
513
514 AgsAudio *audio;
515
516 GtkTreeIter iter;
517
518 guint count;
519
520 connection_editor = AGS_CONNECTION_EDITOR(gtk_widget_get_ancestor(GTK_WIDGET(input_collection_editor),
521 AGS_TYPE_CONNECTION_EDITOR));
522 audio = connection_editor->machine->audio;
523
524 /* get audio fields */
525 if(g_type_is_a(input_collection_editor->channel_type, AGS_TYPE_INPUT)){
526 g_object_get(audio,
527 "input-lines", &count,
528 NULL);
529 }else if(g_type_is_a(input_collection_editor->channel_type, AGS_TYPE_OUTPUT)){
530 g_object_get(audio,
531 "output-lines", &count,
532 NULL);
533 }else{
534 count = 0;
535 }
536
537 gtk_spin_button_set_range(input_collection_editor->first_line,
538 0.0,
539 count - 1.0);
540 gtk_spin_button_set_range(input_collection_editor->count,
541 0.0,
542 count);
543
544 if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(input_collection_editor->soundcard),
545 &iter)){
546 GObject *input_soundcard;
547
548 GtkTreeModel *model;
549
550 guint audio_channels;
551
552 /* soundcard connection */
553 model = gtk_combo_box_get_model(GTK_COMBO_BOX(input_collection_editor->soundcard));
554 gtk_tree_model_get(model,
555 &iter,
556 1, &input_soundcard,
557 -1);
558
559 ags_soundcard_get_presets(AGS_SOUNDCARD(input_soundcard),
560 &audio_channels,
561 NULL,
562 NULL,
563 NULL);
564
565 gtk_spin_button_set_range(input_collection_editor->audio_channel,
566 0.0,
567 audio_channels - 1.0);
568
569 if(audio_channels < count){
570 gtk_spin_button_set_range(input_collection_editor->count,
571 0.0,
572 audio_channels);
573 }
574 }else{
575 gtk_spin_button_set_range(input_collection_editor->audio_channel,
576 -1.0, -1.0);
577 }
578 }
579
580 /**
581 * ags_input_collection_editor_new:
582 * @channel_type: either %AGS_TYPE_INPUT or %AGS_TYPE_OUTPUT
583 *
584 * Create a new instance of #AgsInputCollectionEditor
585 *
586 * Returns: the new #AgsInputCollectionEditor
587 *
588 * Since: 3.0.0
589 */
590 AgsInputCollectionEditor*
ags_input_collection_editor_new(GType channel_type)591 ags_input_collection_editor_new(GType channel_type)
592 {
593 AgsInputCollectionEditor *input_collection_editor;
594
595 input_collection_editor = (AgsInputCollectionEditor *) g_object_new(AGS_TYPE_INPUT_COLLECTION_EDITOR,
596 "channel_type", channel_type,
597 NULL);
598
599 return(input_collection_editor);
600 }
601