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