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", ¬e_x0,
456 "y", ¬e_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