1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2021 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_pad.h>
21 #include <ags/X/ags_pad_callbacks.h>
22 
23 #include <ags/X/ags_ui_provider.h>
24 #include <ags/X/ags_window.h>
25 #include <ags/X/ags_machine.h>
26 
27 #include <ags/i18n.h>
28 
29 void ags_pad_class_init(AgsPadClass *pad);
30 void ags_pad_connectable_interface_init(AgsConnectableInterface *connectable);
31 void ags_pad_init(AgsPad *pad);
32 void ags_pad_set_property(GObject *gobject,
33 			  guint prop_id,
34 			  const GValue *value,
35 			  GParamSpec *param_spec);
36 void ags_pad_get_property(GObject *gobject,
37 			  guint prop_id,
38 			  GValue *value,
39 			  GParamSpec *param_spec);
40 
41 void ags_pad_connect(AgsConnectable *connectable);
42 void ags_pad_disconnect(AgsConnectable *connectable);
43 
44 void ags_pad_real_set_channel(AgsPad *pad, AgsChannel *channel);
45 void ags_pad_real_resize_lines(AgsPad *pad, GType line_type,
46 			       guint audio_channels, guint audio_channels_old);
47 void ags_pad_real_map_recall(AgsPad *pad,
48 			     guint output_pad_start);
49 GList* ags_pad_real_find_port(AgsPad *pad);
50 
51 /**
52  * SECTION:ags_pad
53  * @short_description: A composite widget to visualize a bunch of #AgsChannel
54  * @title: AgsPad
55  * @section_id:
56  * @include: ags/X/ags_pad.h
57  *
58  * #AgsPad is a composite widget to visualize a bunch of #AgsChannel. It should be
59  * packed by an #AgsMachine.
60  */
61 
62 enum{
63   SAMPLERATE_CHANGED,
64   BUFFER_SIZE_CHANGED,
65   FORMAT_CHANGED,
66   SET_CHANNEL,
67   RESIZE_LINES,
68   MAP_RECALL,
69   FIND_PORT,
70   LAST_SIGNAL,
71 };
72 
73 enum{
74   PROP_0,
75   PROP_SAMPLERATE,
76   PROP_BUFFER_SIZE,
77   PROP_FORMAT,
78   PROP_CHANNEL,
79 };
80 
81 static gpointer ags_pad_parent_class = NULL;
82 static guint pad_signals[LAST_SIGNAL];
83 
84 GType
ags_pad_get_type(void)85 ags_pad_get_type(void)
86 {
87   static volatile gsize g_define_type_id__volatile = 0;
88 
89   if(g_once_init_enter (&g_define_type_id__volatile)){
90     GType ags_type_pad = 0;
91 
92     static const GTypeInfo ags_pad_info = {
93       sizeof(AgsPadClass),
94       NULL, /* base_init */
95       NULL, /* base_finalize */
96       (GClassInitFunc) ags_pad_class_init,
97       NULL, /* class_finalize */
98       NULL, /* class_data */
99       sizeof(AgsPad),
100       0,    /* n_preallocs */
101       (GInstanceInitFunc) ags_pad_init,
102     };
103 
104     static const GInterfaceInfo ags_connectable_interface_info = {
105       (GInterfaceInitFunc) ags_pad_connectable_interface_init,
106       NULL, /* interface_finalize */
107       NULL, /* interface_data */
108     };
109 
110     ags_type_pad = g_type_register_static(GTK_TYPE_BOX,
111 					  "AgsPad", &ags_pad_info,
112 					  0);
113 
114     g_type_add_interface_static(ags_type_pad,
115 				AGS_TYPE_CONNECTABLE,
116 				&ags_connectable_interface_info);
117 
118     g_once_init_leave(&g_define_type_id__volatile, ags_type_pad);
119   }
120 
121   return g_define_type_id__volatile;
122 }
123 
124 void
ags_pad_class_init(AgsPadClass * pad)125 ags_pad_class_init(AgsPadClass *pad)
126 {
127   GObjectClass *gobject;
128   GParamSpec *param_spec;
129 
130   ags_pad_parent_class = g_type_class_peek_parent(pad);
131 
132   /* GObjectClass */
133   gobject = G_OBJECT_CLASS(pad);
134 
135   gobject->set_property = ags_pad_set_property;
136   gobject->get_property = ags_pad_get_property;
137 
138   //TODO:JK: add finalize
139 
140   /* properties */
141   /**
142    * AgsPad:samplerate:
143    *
144    * The samplerate.
145    *
146    * Since: 3.0.0
147    */
148   param_spec = g_param_spec_uint("samplerate",
149 				 i18n_pspec("samplerate"),
150 				 i18n_pspec("The samplerate"),
151 				 0,
152 				 G_MAXUINT32,
153 				 AGS_SOUNDCARD_DEFAULT_SAMPLERATE,
154 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
155   g_object_class_install_property(gobject,
156 				  PROP_SAMPLERATE,
157 				  param_spec);
158 
159   /**
160    * AgsPad:buffer-size:
161    *
162    * The buffer length.
163    *
164    * Since: 3.0.0
165    */
166   param_spec = g_param_spec_uint("buffer-size",
167 				 i18n_pspec("buffer size"),
168 				 i18n_pspec("The buffer size"),
169 				 0,
170 				 G_MAXUINT32,
171 				 AGS_SOUNDCARD_DEFAULT_BUFFER_SIZE,
172 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
173   g_object_class_install_property(gobject,
174 				  PROP_BUFFER_SIZE,
175 				  param_spec);
176 
177   /**
178    * AgsPad:format:
179    *
180    * The format.
181    *
182    * Since: 3.0.0
183    */
184   param_spec = g_param_spec_uint("format",
185 				 i18n_pspec("format"),
186 				 i18n_pspec("The format"),
187 				 0,
188 				 G_MAXUINT32,
189 				 AGS_SOUNDCARD_DEFAULT_FORMAT,
190 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
191   g_object_class_install_property(gobject,
192 				  PROP_FORMAT,
193 				  param_spec);
194 
195   /**
196    * AgsPad:channel:
197    *
198    * The start of a bunch of #AgsChannel to visualize.
199    *
200    * Since: 3.0.0
201    */
202   param_spec = g_param_spec_object("channel",
203 				   i18n_pspec("assigned channel"),
204 				   i18n_pspec("The channel it is assigned with"),
205 				   AGS_TYPE_CHANNEL,
206 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
207   g_object_class_install_property(gobject,
208 				  PROP_CHANNEL,
209 				  param_spec);
210 
211   /* AgsPadClass */
212   pad->samplerate_changed = NULL;
213   pad->buffer_size_changed = NULL;
214   pad->format_changed = NULL;
215 
216   pad->set_channel = ags_pad_real_set_channel;
217   pad->resize_lines = ags_pad_real_resize_lines;
218   pad->map_recall = ags_pad_real_map_recall;
219   pad->find_port = ags_pad_real_find_port;
220 
221   /* signals */
222   /**
223    * AgsPad::samplerate-changed:
224    * @pad: the #AgsPad
225    * @samplerate: the samplerate
226    * @old_samplerate: the old samplerate
227    *
228    * The ::samplerate-changed signal notifies about changed samplerate.
229    *
230    * Since: 3.0.0
231    */
232   pad_signals[SAMPLERATE_CHANGED] =
233     g_signal_new("samplerate-changed",
234 		 G_TYPE_FROM_CLASS(pad),
235 		 G_SIGNAL_RUN_LAST,
236 		 G_STRUCT_OFFSET(AgsPadClass, samplerate_changed),
237 		 NULL, NULL,
238 		 ags_cclosure_marshal_VOID__UINT_UINT,
239 		 G_TYPE_NONE, 2,
240 		 G_TYPE_UINT,
241 		 G_TYPE_UINT);
242 
243   /**
244    * AgsPad::buffer-size-changed:
245    * @pad: the #AgsPad
246    * @buffer_size: the buffer size
247    * @old_buffer_size: the old buffer size
248    *
249    * The ::buffer-size-changed signal notifies about changed buffer size.
250    *
251    * Since: 3.0.0
252    */
253   pad_signals[BUFFER_SIZE_CHANGED] =
254     g_signal_new("buffer-size-changed",
255 		 G_TYPE_FROM_CLASS(pad),
256 		 G_SIGNAL_RUN_LAST,
257 		 G_STRUCT_OFFSET(AgsPadClass, buffer_size_changed),
258 		 NULL, NULL,
259 		 ags_cclosure_marshal_VOID__UINT_UINT,
260 		 G_TYPE_NONE, 2,
261 		 G_TYPE_UINT,
262 		 G_TYPE_UINT);
263 
264   /**
265    * AgsPad::format-changed:
266    * @pad: the #AgsPad
267    * @format: the format
268    * @old_format: the old format
269    *
270    * The ::format-changed signal notifies about changed format.
271    *
272    * Since: 3.0.0
273    */
274   pad_signals[FORMAT_CHANGED] =
275     g_signal_new("format-changed",
276 		 G_TYPE_FROM_CLASS(pad),
277 		 G_SIGNAL_RUN_LAST,
278 		 G_STRUCT_OFFSET(AgsPadClass, format_changed),
279 		 NULL, NULL,
280 		 ags_cclosure_marshal_VOID__UINT_UINT,
281 		 G_TYPE_NONE, 2,
282 		 G_TYPE_UINT,
283 		 G_TYPE_UINT);
284 
285   /**
286    * AgsPad::set-channel:
287    * @pad: the #AgsPad to modify
288    * @channel: the #AgsChannel to set
289    *
290    * The ::set-channel signal notifies about changed channel.
291    *
292    * Since: 3.0.0
293    */
294   pad_signals[SET_CHANNEL] =
295     g_signal_new("set-channel",
296 		 G_TYPE_FROM_CLASS(pad),
297 		 G_SIGNAL_RUN_LAST,
298 		 G_STRUCT_OFFSET(AgsPadClass, set_channel),
299 		 NULL, NULL,
300 		 g_cclosure_marshal_VOID__OBJECT,
301 		 G_TYPE_NONE, 1,
302 		 G_TYPE_OBJECT);
303 
304   /**
305    * AgsPad::resize-lines:
306    * @pad: the #AgsPad to resize
307    * @line_type: the channel type
308    * @audio_channels: count of lines
309    * @audio_channels_old: old count of lines
310    *
311    * The ::resize-lines is emitted as count of lines pack is modified.
312    *
313    * Since: 3.0.0
314    */
315   pad_signals[RESIZE_LINES] =
316     g_signal_new("resize-lines",
317 		 G_TYPE_FROM_CLASS(pad),
318 		 G_SIGNAL_RUN_LAST,
319 		 G_STRUCT_OFFSET(AgsPadClass, resize_lines),
320 		 NULL, NULL,
321 		 ags_cclosure_marshal_VOID__ULONG_UINT_UINT,
322 		 G_TYPE_NONE, 3,
323 		 G_TYPE_ULONG, G_TYPE_UINT, G_TYPE_UINT);
324 
325 
326   /**
327    * AgsPad::map-recall:
328    * @pad: the #AgsPad to resize
329    * @output_pad_start: start of output pad
330    *
331    * The ::map-recall as recall should be mapped
332    *
333    * Since: 3.0.0
334    */
335   pad_signals[MAP_RECALL] =
336     g_signal_new("map-recall",
337 		 G_TYPE_FROM_CLASS(pad),
338 		 G_SIGNAL_RUN_LAST,
339 		 G_STRUCT_OFFSET(AgsPadClass, map_recall),
340 		 NULL, NULL,
341 		 g_cclosure_marshal_VOID__UINT,
342 		 G_TYPE_NONE, 1,
343 		 G_TYPE_UINT);
344 
345   /**
346    * AgsPad::find-port:
347    * @pad: the #AgsPad to resize
348    *
349    * The ::find-port retrieves all associated ports
350    *
351    * Returns: a #GList-struct with associated ports
352    *
353    * Since: 3.0.0
354    */
355   pad_signals[FIND_PORT] =
356     g_signal_new("find-port",
357 		 G_TYPE_FROM_CLASS(pad),
358 		 G_SIGNAL_RUN_LAST,
359 		 G_STRUCT_OFFSET(AgsPadClass, find_port),
360 		 NULL, NULL,
361 		 ags_cclosure_marshal_POINTER__VOID,
362 		 G_TYPE_POINTER, 0);
363 }
364 
365 void
ags_pad_connectable_interface_init(AgsConnectableInterface * connectable)366 ags_pad_connectable_interface_init(AgsConnectableInterface *connectable)
367 {
368   connectable->is_ready = NULL;
369   connectable->is_connected = NULL;
370   connectable->connect = ags_pad_connect;
371   connectable->disconnect = ags_pad_disconnect;
372 }
373 
374 void
ags_pad_init(AgsPad * pad)375 ags_pad_init(AgsPad *pad)
376 {
377   GtkBox *hbox;
378 
379   AgsConfig *config;
380 
381   gtk_orientable_set_orientation(GTK_ORIENTABLE(pad),
382 				 GTK_ORIENTATION_VERTICAL);
383 
384   pad->flags = 0;
385 
386   pad->name = NULL;
387 
388   pad->version = AGS_VERSION;
389   pad->build_id = AGS_BUILD_ID;
390 
391   config = ags_config_get_instance();
392 
393   pad->samplerate = ags_soundcard_helper_config_get_samplerate(config);
394   pad->buffer_size = ags_soundcard_helper_config_get_buffer_size(config);
395   pad->format = ags_soundcard_helper_config_get_format(config);
396 
397   pad->channel = NULL;
398 
399   pad->cols = 2;
400 
401   pad->expander_set = ags_expander_set_new(1, 1);
402   gtk_box_pack_start((GtkBox *) pad, (GtkWidget *) pad->expander_set, TRUE, TRUE, 0);
403 
404   hbox = (GtkBox *) gtk_box_new(GTK_ORIENTATION_HORIZONTAL,
405 				0);
406   gtk_box_pack_start((GtkBox *) pad, (GtkWidget *) hbox, FALSE, FALSE, 0);
407 
408   pad->group = (GtkToggleButton *) gtk_toggle_button_new_with_label("G");
409   gtk_toggle_button_set_active(pad->group, TRUE);
410   gtk_box_pack_start((GtkBox *) hbox, (GtkWidget *) pad->group, FALSE, FALSE, 0);
411 
412   pad->mute = (GtkToggleButton *) gtk_toggle_button_new_with_label("M");
413   gtk_box_pack_start((GtkBox *) hbox, (GtkWidget *) pad->mute, FALSE, FALSE, 0);
414 
415   pad->solo = (GtkToggleButton *) gtk_toggle_button_new_with_label("S");
416   gtk_box_pack_start((GtkBox *) hbox, (GtkWidget *) pad->solo, FALSE, FALSE, 0);
417 
418   pad->play = NULL;
419 }
420 
421 void
ags_pad_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)422 ags_pad_set_property(GObject *gobject,
423 		     guint prop_id,
424 		     const GValue *value,
425 		     GParamSpec *param_spec)
426 {
427   AgsPad *pad;
428 
429   pad = AGS_PAD(gobject);
430 
431   switch(prop_id){
432   case PROP_SAMPLERATE:
433     {
434       GList *start_list, *list;
435 
436       guint samplerate, old_samplerate;
437 
438       samplerate = g_value_get_uint(value);
439       old_samplerate = pad->samplerate;
440 
441       if(samplerate == old_samplerate){
442 	return;
443       }
444 
445       pad->samplerate = samplerate;
446 
447       ags_pad_samplerate_changed(pad,
448 				 samplerate, old_samplerate);
449 
450       list =
451 	start_list = gtk_container_get_children((GtkContainer *) pad->expander_set);
452 
453       while(list != NULL){
454 	if(AGS_LINE(list->data)){
455 	  g_object_set(list->data,
456 		       "samplerate", samplerate,
457 		       NULL);
458 	}
459 
460 	list = list->next;
461       }
462 
463       g_list_free(start_list);
464     }
465     break;
466   case PROP_BUFFER_SIZE:
467     {
468       GList *start_list, *list;
469 
470       guint buffer_size, old_buffer_size;
471 
472       buffer_size = g_value_get_uint(value);
473       old_buffer_size = pad->buffer_size;
474 
475       if(buffer_size == old_buffer_size){
476 	return;
477       }
478 
479       pad->buffer_size = buffer_size;
480 
481       ags_pad_buffer_size_changed(pad,
482 				  buffer_size, old_buffer_size);
483 
484       list =
485 	start_list = gtk_container_get_children((GtkContainer *) pad->expander_set);
486 
487       while(list != NULL){
488 	if(AGS_LINE(list->data)){
489 	  g_object_set(list->data,
490 		       "buffer-size", buffer_size,
491 		       NULL);
492 	}
493 
494 	list = list->next;
495       }
496 
497       g_list_free(start_list);
498     }
499     break;
500   case PROP_FORMAT:
501     {
502       GList *start_list, *list;
503 
504       guint format, old_format;
505 
506       format = g_value_get_uint(value);
507       old_format = pad->format;
508 
509       if(format == old_format){
510 	return;
511       }
512 
513       pad->format = format;
514 
515       ags_pad_format_changed(pad,
516 			     format, old_format);
517 
518       list =
519 	start_list = gtk_container_get_children((GtkContainer *) pad->expander_set);
520 
521       while(list != NULL){
522 	if(AGS_LINE(list->data)){
523 	  g_object_set(list->data,
524 		       "format", format,
525 		       NULL);
526 	}
527 
528 	list = list->next;
529       }
530 
531       g_list_free(start_list);
532     }
533     break;
534   case PROP_CHANNEL:
535     {
536       AgsChannel *channel;
537 
538       channel = (AgsChannel *) g_value_get_object(value);
539 
540       ags_pad_set_channel(pad, channel);
541     }
542     break;
543   default:
544     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
545     break;
546   }
547 }
548 
549 void
ags_pad_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)550 ags_pad_get_property(GObject *gobject,
551 		     guint prop_id,
552 		     GValue *value,
553 		     GParamSpec *param_spec)
554 {
555   AgsPad *pad;
556 
557   pad = AGS_PAD(gobject);
558 
559   switch(prop_id){
560   case PROP_SAMPLERATE:
561     {
562       g_value_set_uint(value,
563 		       pad->samplerate);
564     }
565     break;
566   case PROP_BUFFER_SIZE:
567     {
568       g_value_set_uint(value,
569 		       pad->buffer_size);
570     }
571     break;
572   case PROP_FORMAT:
573     {
574       g_value_set_uint(value,
575 		       pad->format);
576     }
577     break;
578   case PROP_CHANNEL:
579     {
580       g_value_set_object(value, pad->channel);
581     }
582     break;
583   default:
584     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
585     break;
586   }
587 }
588 
589 void
ags_pad_connect(AgsConnectable * connectable)590 ags_pad_connect(AgsConnectable *connectable)
591 {
592   AgsPad *pad;
593   GList *line_list, *line_list_start;
594 
595   /* AgsPad */
596   pad = AGS_PAD(connectable);
597 
598   if((AGS_PAD_CONNECTED & (pad->flags)) != 0){
599     return;
600   }
601 
602   pad->flags |= AGS_PAD_CONNECTED;
603 
604   if((AGS_PAD_PREMAPPED_RECALL & (pad->flags)) == 0){
605     if((AGS_PAD_MAPPED_RECALL & (pad->flags)) == 0){
606       ags_pad_map_recall(pad,
607 			 0);
608     }
609   }else{
610     pad->flags &= (~AGS_PAD_PREMAPPED_RECALL);
611 
612     ags_pad_find_port(pad);
613   }
614 
615   /* GtkButton */
616   g_signal_connect_after((GObject *) pad->group, "clicked",
617 			 G_CALLBACK(ags_pad_group_clicked_callback), (gpointer) pad);
618 
619   g_signal_connect_after((GObject *) pad->mute, "clicked",
620 			 G_CALLBACK(ags_pad_mute_clicked_callback), (gpointer) pad);
621 
622   g_signal_connect_after((GObject *) pad->solo, "clicked",
623 			 G_CALLBACK(ags_pad_solo_clicked_callback), (gpointer) pad);
624 
625   /* AgsLine */
626   line_list_start =
627     line_list = gtk_container_get_children(GTK_CONTAINER(pad->expander_set));
628 
629   while(line_list != NULL){
630     ags_connectable_connect(AGS_CONNECTABLE(line_list->data));
631 
632     line_list = line_list->next;
633   }
634 
635   g_list_free(line_list_start);
636 }
637 
638 void
ags_pad_disconnect(AgsConnectable * connectable)639 ags_pad_disconnect(AgsConnectable *connectable)
640 {
641   AgsPad *pad;
642   GList *line_list, *line_list_start;
643 
644   /* AgsPad */
645   pad = AGS_PAD(connectable);
646 
647   if((AGS_PAD_CONNECTED & (pad->flags)) == 0){
648     return;
649   }
650 
651   pad->flags &= (~AGS_PAD_CONNECTED);
652 
653   /* AgsLine */
654   line_list_start =
655     line_list = gtk_container_get_children(GTK_CONTAINER(pad->expander_set));
656 
657   while(line_list != NULL){
658     ags_connectable_disconnect(AGS_CONNECTABLE(line_list->data));
659 
660     line_list = line_list->next;
661   }
662 
663   g_list_free(line_list_start);
664 
665   g_signal_handlers_disconnect_by_data(pad->channel,
666 				       pad);
667 }
668 
669 /**
670  * ags_pad_samplerate_changed:
671  * @pad: the #AgsPad
672  * @samplerate: the samplerate
673  * @old_samplerate: the old samplerate
674  *
675  * Notify about samplerate changed.
676  *
677  * Since: 3.0.0
678  */
679 void
ags_pad_samplerate_changed(AgsPad * pad,guint samplerate,guint old_samplerate)680 ags_pad_samplerate_changed(AgsPad *pad,
681 			   guint samplerate, guint old_samplerate)
682 {
683   g_return_if_fail(AGS_IS_PAD(pad));
684 
685   g_object_ref((GObject *) pad);
686   g_signal_emit(G_OBJECT(pad),
687 		pad_signals[SAMPLERATE_CHANGED], 0,
688 		samplerate,
689 		old_samplerate);
690   g_object_unref((GObject *) pad);
691 }
692 
693 /**
694  * ags_pad_buffer_size_changed:
695  * @pad: the #AgsPad
696  * @buffer_size: the buffer_size
697  * @old_buffer_size: the old buffer_size
698  *
699  * Notify about buffer_size changed.
700  *
701  * Since: 3.0.0
702  */
703 void
ags_pad_buffer_size_changed(AgsPad * pad,guint buffer_size,guint old_buffer_size)704 ags_pad_buffer_size_changed(AgsPad *pad,
705 			    guint buffer_size, guint old_buffer_size)
706 {
707   g_return_if_fail(AGS_IS_PAD(pad));
708 
709   g_object_ref((GObject *) pad);
710   g_signal_emit(G_OBJECT(pad),
711 		pad_signals[BUFFER_SIZE_CHANGED], 0,
712 		buffer_size,
713 		old_buffer_size);
714   g_object_unref((GObject *) pad);
715 }
716 
717 /**
718  * ags_pad_format_changed:
719  * @pad: the #AgsPad
720  * @format: the format
721  * @old_format: the old format
722  *
723  * Notify about format changed.
724  *
725  * Since: 3.0.0
726  */
727 void
ags_pad_format_changed(AgsPad * pad,guint format,guint old_format)728 ags_pad_format_changed(AgsPad *pad,
729 		       guint format, guint old_format)
730 {
731   g_return_if_fail(AGS_IS_PAD(pad));
732 
733   g_object_ref((GObject *) pad);
734   g_signal_emit(G_OBJECT(pad),
735 		pad_signals[FORMAT_CHANGED], 0,
736 		format,
737 		old_format);
738   g_object_unref((GObject *) pad);
739 }
740 
741 void
ags_pad_real_set_channel(AgsPad * pad,AgsChannel * channel)742 ags_pad_real_set_channel(AgsPad *pad, AgsChannel *channel)
743 {
744   AgsChannel *current, *next_current;
745 
746   GList *line, *line_start;
747 
748   if(pad->channel == channel){
749     return;
750   }
751 
752   if(pad->channel != NULL){
753     g_object_unref(G_OBJECT(pad->channel));
754   }
755 
756   if(channel != NULL){
757     g_object_ref(G_OBJECT(channel));
758   }
759 
760   if(channel != NULL){
761     pad->samplerate = channel->samplerate;
762     pad->buffer_size = channel->buffer_size;
763     pad->format = channel->format;
764   }
765 
766   pad->channel = channel;
767 
768   line_start =
769     line = gtk_container_get_children(GTK_CONTAINER(AGS_PAD(pad)->expander_set));
770 
771   current = channel;
772 
773   if(current != NULL){
774     g_object_ref(current);
775   }
776 
777   next_current = NULL;
778 
779   /* set channel */
780   while(line != NULL){
781     g_object_set(G_OBJECT(line->data),
782 		 "channel", current,
783 		 NULL);
784 
785     /* iterate */
786     if(current != NULL){
787       next_current = ags_channel_next(current);
788 
789       g_object_unref(current);
790 
791       current = next_current;
792     }
793 
794     line = line->next;
795   }
796 
797   if(next_current != NULL){
798     g_object_unref(next_current);
799   }
800 
801   g_list_free(line_start);
802 }
803 
804 /**
805  * ags_pad_set_channel:
806  * @pad: an #AgsPad
807  * @channel: the #AgsChannel to set
808  *
809  * Is emitted as channel gets modified.
810  *
811  * Since: 3.0.0
812  */
813 void
ags_pad_set_channel(AgsPad * pad,AgsChannel * channel)814 ags_pad_set_channel(AgsPad *pad, AgsChannel *channel)
815 {
816   g_return_if_fail(AGS_IS_PAD(pad));
817 
818   g_object_ref((GObject *) pad);
819   g_signal_emit(G_OBJECT(pad),
820 		pad_signals[SET_CHANNEL], 0,
821 		channel);
822   g_object_unref((GObject *) pad);
823 }
824 
825 void
ags_pad_real_resize_lines(AgsPad * pad,GType line_type,guint audio_channels,guint audio_channels_old)826 ags_pad_real_resize_lines(AgsPad *pad, GType line_type,
827 			  guint audio_channels, guint audio_channels_old)
828 {
829   AgsLine *line;
830 
831   AgsAudio *audio;
832   AgsChannel *channel;
833 
834   guint audio_audio_channels;
835   guint i;
836 
837 #ifdef AGS_DEBUG
838   g_message("ags_pad_real_resize_lines: audio_channels = %u ; audio_channels_old = %u\n", audio_channels, audio_channels_old);
839 #endif
840 
841   audio = NULL;
842 
843   audio_audio_channels = 0;
844 
845   if(pad->channel != NULL){
846     g_object_get(pad->channel,
847 		 "audio", &audio,
848 		 NULL);
849   }
850 
851   if(audio != NULL){
852     g_object_get(audio,
853 		 "audio-channels", &audio_audio_channels,
854 		 NULL);
855   }
856 
857   /* resize */
858   if(audio_channels > audio_channels_old){
859     /* create AgsLine */
860     for(i = audio_channels_old; i < audio_channels; i++){
861       /* instantiate line */
862       if(i < audio_audio_channels){
863 	channel = ags_channel_nth(pad->channel,
864 				  i);
865       }else{
866 	channel = NULL;
867       }
868 
869       line = (AgsLine *) g_object_new(line_type,
870 				      "pad", pad,
871 				      "channel", channel,
872 				      NULL);
873 
874       if(channel != NULL){
875 	channel->line_widget = (GObject *) line;
876       }
877 
878       ags_expander_set_add(pad->expander_set,
879 			   (GtkWidget *) line,
880 			   i % pad->cols, floor(i / pad->cols),
881 			   1, 1);
882 
883       if(channel != NULL){
884 	g_object_unref(channel);
885       }
886     }
887   }else if(audio_channels < audio_channels_old){
888     GList *list, *list_start;
889 
890     list_start =
891       list = g_list_nth(g_list_reverse(gtk_container_get_children(GTK_CONTAINER(pad->expander_set))),
892 			audio_channels);
893 
894     while(list != NULL){
895       ags_connectable_disconnect(AGS_CONNECTABLE(list->data));
896 
897       list = list->next;
898     }
899 
900     list = list_start;
901 
902     while(list != NULL){
903       gtk_widget_destroy(GTK_WIDGET(list->data));
904 
905       list = list->next;
906     }
907 
908     g_list_free(list_start);
909   }
910 
911   if(audio != NULL){
912     g_object_unref(audio);
913   }
914 }
915 
916 /**
917  * ags_pad_resize_lines:
918  * @pad: the #AgsPad to resize
919  * @line_type: channel type, either %AGS_TYPE_INPUT or %AGS_TYPE_OUTPUT
920  * @audio_channels: count of lines
921  * @audio_channels_old: old count of lines
922  *
923  * Resize the count of #AgsLine packe by #AgsPad.
924  *
925  * Since: 3.0.0
926  */
927 void
ags_pad_resize_lines(AgsPad * pad,GType line_type,guint audio_channels,guint audio_channels_old)928 ags_pad_resize_lines(AgsPad *pad, GType line_type,
929 		     guint audio_channels, guint audio_channels_old)
930 {
931   g_return_if_fail(AGS_IS_PAD(pad));
932 
933   //  fprintf(stdout, "ags_pad_resize_lines: audio_channels = %u ; audio_channels_old = %u\n", audio_channels, audio_channels_old);
934 
935   g_object_ref((GObject *) pad);
936   g_signal_emit(G_OBJECT(pad),
937 		pad_signals[RESIZE_LINES], 0,
938 		line_type,
939 		audio_channels, audio_channels_old);
940   g_object_unref((GObject *) pad);
941 }
942 
943 void
ags_pad_real_map_recall(AgsPad * pad,guint output_pad_start)944 ags_pad_real_map_recall(AgsPad *pad, guint output_pad_start)
945 {
946   if((AGS_PAD_MAPPED_RECALL & (pad->flags)) != 0){
947     return;
948   }
949 
950   pad->flags |= AGS_PAD_MAPPED_RECALL;
951 
952   ags_pad_find_port(pad);
953 }
954 
955 /**
956  * ags_pad_map_recall:
957  * @pad: the #AgsPad to resize
958  * @output_pad_start: start of output pad
959  *
960  * Start of output pad
961  *
962  * Since: 3.0.0
963  */
964 void
ags_pad_map_recall(AgsPad * pad,guint output_pad_start)965 ags_pad_map_recall(AgsPad *pad, guint output_pad_start)
966 {
967   g_return_if_fail(AGS_IS_PAD(pad));
968 
969   g_object_ref((GObject *) pad);
970   g_signal_emit(G_OBJECT(pad),
971 		pad_signals[MAP_RECALL], 0,
972 		output_pad_start);
973   g_object_unref((GObject *) pad);
974 }
975 
976 GList*
ags_pad_real_find_port(AgsPad * pad)977 ags_pad_real_find_port(AgsPad *pad)
978 {
979   GList *start_line, *line;
980 
981   GList *port, *tmp_port;
982 
983   port = NULL;
984 
985   /* find output ports */
986   if(pad->expander_set != NULL){
987     line =
988       start_line = gtk_container_get_children((GtkContainer *) pad->expander_set);
989 
990     while(line != NULL){
991       tmp_port = ags_line_find_port(AGS_LINE(line->data));
992 
993       if(port != NULL){
994 	port = g_list_concat(port,
995 			     tmp_port);
996       }else{
997 	port = tmp_port;
998       }
999 
1000       line = line->next;
1001     }
1002 
1003     g_list_free(start_line);
1004   }
1005 
1006   return(port);
1007 }
1008 
1009 /**
1010  * ags_pad_find_port:
1011  * @pad: an #AgsPad
1012  *
1013  * Lookup ports of assigned recalls.
1014  *
1015  * Returns: an #GList containing all related #AgsPort
1016  *
1017  * Since: 3.0.0
1018  */
1019 GList*
ags_pad_find_port(AgsPad * pad)1020 ags_pad_find_port(AgsPad *pad)
1021 {
1022   GList *list;
1023 
1024   list = NULL;
1025   g_return_val_if_fail(AGS_IS_PAD(pad),
1026 		       NULL);
1027 
1028   g_object_ref((GObject *) pad);
1029   g_signal_emit((GObject *) pad,
1030 		pad_signals[FIND_PORT], 0,
1031 		&list);
1032   g_object_unref((GObject *) pad);
1033 
1034   return(list);
1035 }
1036 
1037 void
ags_pad_play(AgsPad * pad)1038 ags_pad_play(AgsPad *pad)
1039 {
1040   AgsMachine *machine;
1041 
1042   AgsChannel *channel;
1043   AgsChannel *next_pad, *next_channel;
1044   AgsPlayback *playback;
1045 
1046   GList *start_list, *list;
1047 
1048   gboolean play_all;
1049 
1050   if(!AGS_IS_PAD(pad)){
1051     return;
1052   }
1053 
1054   machine = (AgsMachine *) gtk_widget_get_ancestor((GtkWidget *) pad,
1055 						   AGS_TYPE_MACHINE);
1056 
1057   list =
1058     start_list = gtk_container_get_children(GTK_CONTAINER(pad->expander_set));
1059 
1060   /*  */
1061   play_all = gtk_toggle_button_get_active(pad->group);
1062 
1063   if(gtk_toggle_button_get_active(pad->play)){
1064     if(play_all){
1065       channel = pad->channel;
1066 
1067       if(channel != NULL){
1068 	g_object_ref(channel);
1069       }
1070 
1071       next_pad = ags_channel_next_pad(channel);
1072       next_channel = NULL;
1073 
1074       while(channel != next_pad){
1075 	AgsNote *play_note;
1076 
1077 	g_object_get(channel,
1078 		     "playback", &playback,
1079 		     NULL);
1080 
1081 	g_object_get(playback,
1082 		     "play-note", &play_note,
1083 		     NULL);
1084 
1085 	g_object_set(play_note,
1086 		     "x0", 0,
1087 		     "x1", 1,
1088 		     NULL);
1089 
1090 	ags_machine_playback_set_active(machine,
1091 					playback,
1092 					TRUE);
1093 
1094 	g_object_unref(playback);
1095 
1096 	/* iterate */
1097 	next_channel = ags_channel_next(channel);
1098 
1099 	g_object_unref(channel);
1100 
1101 	channel = next_channel;
1102       }
1103 
1104       /* unref */
1105       if(next_pad != NULL){
1106 	g_object_unref(next_pad);
1107       }
1108 
1109       if(next_channel != NULL){
1110 	g_object_unref(next_channel);
1111       }
1112     }else{
1113       while((list = ags_line_find_next_grouped(list)) != NULL){
1114 	AgsLine *line;
1115 
1116 	line = AGS_LINE(list->data);
1117 
1118 	channel = line->channel;
1119 
1120 	g_object_get(channel,
1121 		     "playback", &playback,
1122 		     NULL);
1123 
1124 	ags_machine_playback_set_active(machine,
1125 					playback,
1126 					TRUE);
1127 
1128 	g_object_unref(playback);
1129 
1130 	/* iterate */
1131 	list = list->next;
1132       }
1133     }
1134   }else{
1135     if(play_all){
1136       channel = pad->channel;
1137 
1138       if(channel != NULL){
1139 	g_object_ref(channel);
1140       }
1141 
1142       next_pad = ags_channel_next_pad(channel);
1143       next_channel = NULL;
1144 
1145       while(channel != next_pad){
1146 	g_object_get(channel,
1147 		     "playback", &playback,
1148 		     NULL);
1149 
1150 	ags_machine_playback_set_active(machine,
1151 					playback,
1152 					FALSE);
1153 
1154 	g_object_unref(playback);
1155 
1156 	/* iterate */
1157 	next_channel = ags_channel_next(channel);
1158 
1159 	g_object_unref(channel);
1160 
1161 	channel = next_channel;
1162       }
1163 
1164       /* unref */
1165       if(next_pad != NULL){
1166 	g_object_unref(next_pad);
1167       }
1168 
1169       if(next_channel != NULL){
1170 	g_object_unref(next_channel);
1171       }
1172     }else{
1173       while((list = ags_line_find_next_grouped(list)) != NULL){
1174 	AgsLine *line;
1175 
1176 	line = AGS_LINE(list->data);
1177 
1178 	channel = line->channel;
1179 
1180 	g_object_get(channel,
1181 		     "playback", &playback,
1182 		     NULL);
1183 
1184 	ags_machine_playback_set_active(machine,
1185 					playback,
1186 					FALSE);
1187 
1188 	g_object_unref(playback);
1189 
1190 	/* iterate */
1191 	list = list->next;
1192       }
1193     }
1194   }
1195 
1196   g_list_free(start_list);
1197 }
1198 
1199 /**
1200  * ags_pad_new:
1201  * @channel: the bunch of channel to visualize
1202  *
1203  * Creates an #AgsPad
1204  *
1205  * Returns: a new #AgsPad
1206  *
1207  * Since: 3.0.0
1208  */
1209 AgsPad*
ags_pad_new(AgsChannel * channel)1210 ags_pad_new(AgsChannel *channel)
1211 {
1212   AgsPad *pad;
1213 
1214   pad = (AgsPad *) g_object_new(AGS_TYPE_PAD, NULL);
1215 
1216   return(pad);
1217 }
1218