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/export/ags_machine_collection_entry.h>
21 
22 #include <ags/X/ags_window.h>
23 #include <ags/X/ags_machine.h>
24 
25 #include <ags/X/export/ags_midi_export_wizard.h>
26 
27 #include <math.h>
28 
29 #include <ags/i18n.h>
30 
31 void ags_machine_collection_entry_class_init(AgsMachineCollectionEntryClass *machine_collection_entry);
32 void ags_machine_collection_entry_connectable_interface_init(AgsConnectableInterface *connectable);
33 void ags_machine_collection_entry_applicable_interface_init(AgsApplicableInterface *applicable);
34 void ags_machine_collection_entry_init(AgsMachineCollectionEntry *machine_collection_entry);
35 void ags_machine_collection_entry_set_property(GObject *gobject,
36 					       guint prop_id,
37 					       const GValue *value,
38 					       GParamSpec *param_spec);
39 void ags_machine_collection_entry_get_property(GObject *gobject,
40 					       guint prop_id,
41 					       GValue *value,
42 					       GParamSpec *param_spec);
43 
44 void ags_machine_collection_entry_connect(AgsConnectable *connectable);
45 void ags_machine_collection_entry_disconnect(AgsConnectable *connectable);
46 
47 void ags_machine_collection_entry_set_update(AgsApplicable *applicable, gboolean update);
48 void ags_machine_collection_entry_apply(AgsApplicable *applicable);
49 void ags_machine_collection_entry_reset(AgsApplicable *applicable);
50 
51 /**
52  * SECTION:ags_machine_collection_entry
53  * @short_description: Machine entry
54  * @title: AgsMachineCollectionEntry
55  * @section_id:
56  * @include: ags/X/ags_machine_collection_entry.h
57  *
58  * #AgsMachineCollectionEntry is a composite widget specifying machines
59  * to export.
60  */
61 
62 enum{
63   PROP_0,
64   PROP_MACHINE,
65 };
66 
67 GType
ags_machine_collection_entry_get_type(void)68 ags_machine_collection_entry_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_machine_collection_entry = 0;
74 
75     static const GTypeInfo ags_machine_collection_entry_info = {
76       sizeof (AgsMachineCollectionEntryClass),
77       NULL, /* base_init */
78       NULL, /* base_finalize */
79       (GClassInitFunc) ags_machine_collection_entry_class_init,
80       NULL, /* class_finalize */
81       NULL, /* class_data */
82       sizeof (AgsMachineCollectionEntry),
83       0,    /* n_preallocs */
84       (GInstanceInitFunc) ags_machine_collection_entry_init,
85     };
86 
87     static const GInterfaceInfo ags_connectable_interface_info = {
88       (GInterfaceInitFunc) ags_machine_collection_entry_connectable_interface_init,
89       NULL, /* interface_finalize */
90       NULL, /* interface_data */
91     };
92 
93     static const GInterfaceInfo ags_applicable_interface_info = {
94       (GInterfaceInitFunc) ags_machine_collection_entry_applicable_interface_init,
95       NULL, /* interface_finalize */
96       NULL, /* interface_data */
97     };
98 
99     ags_type_machine_collection_entry = g_type_register_static(GTK_TYPE_GRID,
100 							       "AgsMachineCollectionEntry", &ags_machine_collection_entry_info,
101 							       0);
102 
103     g_type_add_interface_static(ags_type_machine_collection_entry,
104 				AGS_TYPE_CONNECTABLE,
105 				&ags_connectable_interface_info);
106 
107     g_type_add_interface_static(ags_type_machine_collection_entry,
108 				AGS_TYPE_APPLICABLE,
109 				&ags_applicable_interface_info);
110 
111     g_once_init_leave(&g_define_type_id__volatile, ags_type_machine_collection_entry);
112   }
113 
114   return g_define_type_id__volatile;
115 }
116 
117 void
ags_machine_collection_entry_class_init(AgsMachineCollectionEntryClass * machine_collection_entry)118 ags_machine_collection_entry_class_init(AgsMachineCollectionEntryClass *machine_collection_entry)
119 {
120   GObjectClass *gobject;
121   GParamSpec *param_spec;
122 
123   /* GObjectClass */
124   gobject = (GObjectClass *) machine_collection_entry;
125 
126   gobject->set_property = ags_machine_collection_entry_set_property;
127   gobject->get_property = ags_machine_collection_entry_get_property;
128 
129   /* properties */
130   /**
131    * AgsMachineCollectionEntry:machine:
132    *
133    * The assigned #AgsMachine.
134    *
135    * Since: 3.0.0
136    */
137   param_spec = g_param_spec_object("machine",
138 				   i18n_pspec("assigned machine"),
139 				   i18n_pspec("The machine which this machine entry is assigned with"),
140 				   AGS_TYPE_MACHINE,
141 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
142   g_object_class_install_property(gobject,
143 				  PROP_MACHINE,
144 				  param_spec);
145 }
146 
147 void
ags_machine_collection_entry_connectable_interface_init(AgsConnectableInterface * connectable)148 ags_machine_collection_entry_connectable_interface_init(AgsConnectableInterface *connectable)
149 {
150   connectable->is_ready = NULL;
151   connectable->is_connected = NULL;
152   connectable->connect = ags_machine_collection_entry_connect;
153   connectable->disconnect = ags_machine_collection_entry_disconnect;
154 }
155 
156 void
ags_machine_collection_entry_applicable_interface_init(AgsApplicableInterface * applicable)157 ags_machine_collection_entry_applicable_interface_init(AgsApplicableInterface *applicable)
158 {
159   applicable->set_update = ags_machine_collection_entry_set_update;
160   applicable->apply = ags_machine_collection_entry_apply;
161   applicable->reset = ags_machine_collection_entry_reset;
162 }
163 
164 void
ags_machine_collection_entry_init(AgsMachineCollectionEntry * machine_collection_entry)165 ags_machine_collection_entry_init(AgsMachineCollectionEntry *machine_collection_entry)
166 {
167   GtkLabel *label;
168 
169   machine_collection_entry->flags = 0;
170 
171   machine_collection_entry->machine = NULL;
172 
173   /* enabled */
174   machine_collection_entry->enabled = (GtkCheckButton *) gtk_check_button_new_with_label(i18n("enabled"));
175 
176   gtk_widget_set_valign((GtkWidget *) machine_collection_entry->enabled,
177 			GTK_ALIGN_FILL);
178   gtk_widget_set_halign((GtkWidget *) machine_collection_entry->enabled,
179 			GTK_ALIGN_FILL);
180 
181   gtk_grid_attach((GtkGrid *) machine_collection_entry,
182 		  (GtkWidget *) machine_collection_entry->enabled,
183 		  0, 0,
184 		  4, 1);
185 
186   /* machine label */
187   machine_collection_entry->label = (GtkLabel *) g_object_new(GTK_TYPE_LABEL,
188 							      "xalign", 0.0,
189 							      NULL);
190 
191   gtk_widget_set_valign((GtkWidget *) machine_collection_entry->label,
192 			GTK_ALIGN_FILL);
193   gtk_widget_set_halign((GtkWidget *) machine_collection_entry->label,
194 			GTK_ALIGN_FILL);
195 
196   gtk_grid_attach((GtkGrid *) machine_collection_entry,
197 		  (GtkWidget *) machine_collection_entry->label,
198 		  0, 1,
199 		  4, 1);
200 
201   machine_collection_entry->instrument = NULL;
202 
203   /* sequence */
204   label = (GtkLabel *) g_object_new(GTK_TYPE_LABEL,
205 				    "label", i18n("sequence: "),
206 				    "xalign", 0.0,
207 				    NULL);
208 
209   gtk_widget_set_valign((GtkWidget *) label,
210 			GTK_ALIGN_FILL);
211   gtk_widget_set_halign((GtkWidget *) label,
212 			GTK_ALIGN_FILL);
213 
214   gtk_grid_attach((GtkGrid *) machine_collection_entry,
215 		  (GtkWidget *) label,
216 		  0, 3,
217 		  2, 1);
218 
219   machine_collection_entry->sequence = (GtkEntry *) gtk_entry_new();
220 
221   gtk_widget_set_valign((GtkWidget *) machine_collection_entry->sequence,
222 			GTK_ALIGN_FILL);
223   gtk_widget_set_halign((GtkWidget *) machine_collection_entry->sequence,
224 			GTK_ALIGN_FILL);
225 
226   gtk_grid_attach((GtkGrid *) machine_collection_entry,
227 		  (GtkWidget *) machine_collection_entry->sequence,
228 		  2, 3,
229 		  2, 1);
230 }
231 
232 void
ags_machine_collection_entry_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)233 ags_machine_collection_entry_set_property(GObject *gobject,
234 					  guint prop_id,
235 					  const GValue *value,
236 					  GParamSpec *param_spec)
237 {
238   AgsMachineCollectionEntry *machine_collection_entry;
239 
240   machine_collection_entry = AGS_MACHINE_COLLECTION_ENTRY(gobject);
241 
242   switch(prop_id){
243   case PROP_MACHINE:
244     {
245       GtkWidget *machine;
246 
247       machine = (GtkWidget *) g_value_get_object(value);
248 
249       if(machine_collection_entry->machine == machine){
250 	return;
251       }
252 
253       if(machine_collection_entry->machine != NULL){
254 	g_object_unref(machine_collection_entry->machine);
255       }
256 
257       if(machine != NULL){
258 	g_object_ref(machine);
259 
260 	/* fill in some fields */
261 	gtk_label_set_text(machine_collection_entry->label,
262 			   g_strdup_printf("%s: %s",
263 					   G_OBJECT_TYPE_NAME(machine),
264 					   AGS_MACHINE(machine)->machine_name));
265 
266 	//	gtk_entry_set_text(machine_collection_entry->instrument,
267 	//		   G_OBJECT_TYPE_NAME(machine));
268 
269       	gtk_entry_set_text(machine_collection_entry->sequence,
270 			   AGS_MACHINE(machine)->machine_name);
271       }
272 
273       machine_collection_entry->machine = machine;
274     }
275     break;
276   default:
277     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
278     break;
279   }
280 }
281 
282 void
ags_machine_collection_entry_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)283 ags_machine_collection_entry_get_property(GObject *gobject,
284 					  guint prop_id,
285 					  GValue *value,
286 					  GParamSpec *param_spec)
287 {
288   AgsMachineCollectionEntry *machine_collection_entry;
289 
290   machine_collection_entry = AGS_MACHINE_COLLECTION_ENTRY(gobject);
291 
292   switch(prop_id){
293   case PROP_MACHINE:
294     {
295       g_value_set_object(value, machine_collection_entry->machine);
296     }
297     break;
298   default:
299     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
300     break;
301   }
302 }
303 
304 void
ags_machine_collection_entry_connect(AgsConnectable * connectable)305 ags_machine_collection_entry_connect(AgsConnectable *connectable)
306 {
307   AgsMachineCollectionEntry *machine_collection_entry;
308 
309   machine_collection_entry = AGS_MACHINE_COLLECTION_ENTRY(connectable);
310 
311   if((AGS_MACHINE_COLLECTION_ENTRY_CONNECTED & (machine_collection_entry->flags)) != 0){
312     return;
313   }
314 
315   machine_collection_entry->flags |= AGS_MACHINE_COLLECTION_ENTRY_CONNECTED;
316 }
317 
318 void
ags_machine_collection_entry_disconnect(AgsConnectable * connectable)319 ags_machine_collection_entry_disconnect(AgsConnectable *connectable)
320 {
321   AgsMachineCollectionEntry *machine_collection_entry;
322 
323   machine_collection_entry = AGS_MACHINE_COLLECTION_ENTRY(connectable);
324 
325   if((AGS_MACHINE_COLLECTION_ENTRY_CONNECTED & (machine_collection_entry->flags)) == 0){
326     return;
327   }
328 
329   machine_collection_entry->flags &= (~AGS_MACHINE_COLLECTION_ENTRY_CONNECTED);
330 }
331 
332 void
ags_machine_collection_entry_set_update(AgsApplicable * applicable,gboolean update)333 ags_machine_collection_entry_set_update(AgsApplicable *applicable, gboolean update)
334 {
335   //TODO:JK: implement me
336 }
337 
338 void
ags_machine_collection_entry_apply(AgsApplicable * applicable)339 ags_machine_collection_entry_apply(AgsApplicable *applicable)
340 {
341   AgsMachine *machine;
342 
343   AgsMidiExportWizard *midi_export_wizard;
344   AgsMachineCollectionEntry *machine_collection_entry;
345 
346   AgsMidiBuilder *midi_builder;
347 
348   GList *start_notation, *notation;
349 
350   gchar *segmentation;
351 
352   guint audio_channels;
353   gdouble delay_factor;
354   gdouble bpm;
355   guint prev_delta_time;
356   guint delta_time;
357   guint i;
358 
359   machine_collection_entry = AGS_MACHINE_COLLECTION_ENTRY(applicable);
360 
361   if(!gtk_toggle_button_get_active((GtkToggleButton *) machine_collection_entry->enabled)){
362     return;
363   }
364 
365   midi_export_wizard = (AgsMidiExportWizard *) gtk_widget_get_ancestor((GtkWidget *) machine_collection_entry,
366 								       AGS_TYPE_MIDI_EXPORT_WIZARD);
367 
368   machine = (AgsMachine *) machine_collection_entry->machine;
369 
370   g_object_get(machine->audio,
371 	       "notation", &start_notation,
372 	       "audio-channels", &audio_channels,
373 	       NULL);
374 
375   midi_builder = midi_export_wizard->midi_builder;
376 
377   bpm = midi_builder->midi_header->beat;
378 
379   delay_factor = AGS_SOUNDCARD_DEFAULT_DELAY_FACTOR;
380 
381   /* segmentation */
382   segmentation = ags_config_get_value(ags_config_get_instance(),
383 				      AGS_CONFIG_GENERIC,
384 				      "segmentation");
385 
386   if(segmentation != NULL){
387     guint denominator, numerator;
388 
389     sscanf(segmentation, "%d/%d",
390 	   &denominator,
391 	   &numerator);
392 
393     delay_factor = 1.0 / numerator * (numerator / denominator);
394 
395     g_free(segmentation);
396   }
397 
398   delta_time = 0;
399   prev_delta_time = 0;
400 
401   for(i = 0; i < audio_channels; i++){
402     GList *start_note, *note;
403     GList *start_active_note, *active_note;
404 
405     gchar *str;
406     gint key_on[128];
407 
408     /* append track */
409     str = g_strdup_printf("%s #%d",
410 			  gtk_entry_get_text(machine_collection_entry->sequence),
411 			  i);
412 
413     ags_midi_builder_append_track(midi_builder,
414 				  str);
415 
416     g_free(str);
417 
418     /* append tempo */
419     ags_midi_builder_append_time_signature(midi_builder,
420 					   0,
421 					   4, 4,
422 					   36, 8);
423 
424     notation = start_notation;
425 
426     start_active_note = NULL;
427 
428     /* put keys */
429     memset(key_on, 0, 128 * sizeof(gint));
430 
431     while(notation != NULL){
432       guint audio_channel;
433 
434       g_object_get(notation->data,
435 		   "audio-channel", &audio_channel,
436 		   NULL);
437 
438       if(i != audio_channel){
439 	notation = notation->next;
440 
441 	continue;
442       }
443 
444       g_object_get(notation->data,
445 		   "note", &start_note,
446 		   NULL);
447 
448       note = start_note;
449 
450       while(note != NULL){
451 	guint note_x0;
452 	guint note_y;
453 
454 	g_object_get(note->data,
455 		     "x0", &note_x0,
456 		     "y", &note_y,
457 		     NULL);
458 
459 	/* check note-off */
460 	active_note = start_active_note;
461 
462 	while(active_note != NULL){
463 	  guint active_x1;
464 	  guint active_y;
465 
466 	  g_object_get(active_note->data,
467 		       "x1", &active_x1,
468 		       "y", &active_y,
469 		       NULL);
470 
471 
472 	  if(active_x1 <= note_x0){
473 	    key_on[active_y] -= 1;
474 
475 	    if(key_on[note_y] == 0){
476 	      delta_time = ags_midi_util_offset_to_delta_time(delay_factor,
477 							      AGS_MIDI_EXPORT_WIZARD_DEFAULT_DIVISION,
478 							      AGS_MIDI_EXPORT_WIZARD_DEFAULT_TEMPO,
479 							      AGS_MIDI_EXPORT_WIZARD_DEFAULT_BPM,
480 							      note_x0);
481 
482 	      /* append key-off */
483 	      ags_midi_builder_append_key_off(midi_builder,
484 					      delta_time - prev_delta_time,
485 					      0,
486 					      active_y,
487 					      (guint) 127);  /* * AGS_NOTE(active_note->data)->release.imag */
488 
489 	      prev_delta_time = delta_time;
490 
491 	      start_active_note = g_list_remove(start_active_note,
492 						active_note->data);
493 	    }
494 	  }
495 
496 	  active_note = active_note->next;
497 	}
498 
499 	/* note-on */
500 	if(note_y < 128){
501 	  start_active_note = g_list_prepend(start_active_note,
502 					     note->data);
503 
504 	  key_on[note_y] += 1;
505 
506 	  if(key_on[note_y] == 1){
507 	    delta_time = ags_midi_util_offset_to_delta_time(delay_factor,
508 							    AGS_MIDI_EXPORT_WIZARD_DEFAULT_DIVISION,
509 							    AGS_MIDI_EXPORT_WIZARD_DEFAULT_TEMPO,
510 							    AGS_MIDI_EXPORT_WIZARD_DEFAULT_BPM,
511 							    note_x0);
512 
513 	    /* append key-on */
514 	    ags_midi_builder_append_key_on(midi_builder,
515 					   delta_time - prev_delta_time,
516 					   0,
517 					   note_y,
518 					   (guint) 127); /*  * AGS_NOTE(note->data)->attack.imag */
519 
520 	    prev_delta_time = delta_time;
521 	  }
522 	}
523 
524 	note = note->next;
525       }
526 
527       g_list_free_full(start_note,
528 		       g_object_unref);
529 
530       notation = notation->next;
531     }
532   }
533 
534   g_list_free_full(start_notation,
535 		   g_object_unref);
536 }
537 
538 void
ags_machine_collection_entry_reset(AgsApplicable * applicable)539 ags_machine_collection_entry_reset(AgsApplicable *applicable)
540 {
541   //TODO:JK: implement me
542 }
543 
544 /**
545  * ags_machine_collection_entry_new:
546  * @machine: the #AgsMachine
547  *
548  * Create a new instance of #AgsMachineCollectionEntry
549  *
550  * Returns: the new #AgsMachineCollectionEntry
551  *
552  * Since: 3.0.0
553  */
554 AgsMachineCollectionEntry*
ags_machine_collection_entry_new(GtkWidget * machine)555 ags_machine_collection_entry_new(GtkWidget *machine)
556 {
557   AgsMachineCollectionEntry *machine_collection_entry;
558 
559   machine_collection_entry = (AgsMachineCollectionEntry *) g_object_new(AGS_TYPE_MACHINE_COLLECTION_ENTRY,
560 									"machine", machine,
561 									NULL);
562 
563   return(machine_collection_entry);
564 }
565