1 /* GSequencer - Advanced GTK Sequencer
2 * Copyright (C) 2005-2019 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/audio/recall/ags_copy_audio_signal.h>
21
22 #include <ags/audio/ags_sound_enums.h>
23 #include <ags/audio/ags_channel.h>
24 #include <ags/audio/ags_input.h>
25 #include <ags/audio/ags_recycling.h>
26 #include <ags/audio/ags_audio_signal.h>
27 #include <ags/audio/ags_recall_id.h>
28 #include <ags/audio/ags_recall_channel.h>
29 #include <ags/audio/ags_recall_channel_run.h>
30 #include <ags/audio/ags_audio_buffer_util.h>
31
32 #include <ags/audio/recall/ags_copy_channel.h>
33 #include <ags/audio/recall/ags_copy_channel_run.h>
34 #include <ags/audio/recall/ags_copy_recycling.h>
35
36 #include <stdlib.h>
37
38 void ags_copy_audio_signal_class_init(AgsCopyAudioSignalClass *copy_audio_signal);
39 void ags_copy_audio_signal_init(AgsCopyAudioSignal *copy_audio_signal);
40 void ags_copy_audio_signal_finalize(GObject *gobject);
41
42 void ags_copy_audio_signal_run_init_pre(AgsRecall *recall);
43 void ags_copy_audio_signal_run_pre(AgsRecall *recall);
44 void ags_copy_audio_signal_run_inter(AgsRecall *recall);
45
46 /**
47 * SECTION:ags_copy_audio_signal
48 * @short_description: copy audio signal
49 * @title: AgsCopyAudioSignal
50 * @section_id:
51 * @include: ags/audio/recall/ags_copy_audio_signal.h
52 *
53 * The #AgsCopyAudioSignal class copies the audio signal.
54 */
55
56 static gpointer ags_copy_audio_signal_parent_class = NULL;
57
58 GType
ags_copy_audio_signal_get_type()59 ags_copy_audio_signal_get_type()
60 {
61 static volatile gsize g_define_type_id__volatile = 0;
62
63 if(g_once_init_enter (&g_define_type_id__volatile)){
64 GType ags_type_copy_audio_signal = 0;
65
66 static const GTypeInfo ags_copy_audio_signal_info = {
67 sizeof(AgsCopyAudioSignalClass),
68 NULL, /* base_init */
69 NULL, /* base_finalize */
70 (GClassInitFunc) ags_copy_audio_signal_class_init,
71 NULL, /* class_finalize */
72 NULL, /* class_data */
73 sizeof(AgsCopyAudioSignal),
74 0, /* n_preallocs */
75 (GInstanceInitFunc) ags_copy_audio_signal_init,
76 };
77
78 ags_type_copy_audio_signal = g_type_register_static(AGS_TYPE_RECALL_AUDIO_SIGNAL,
79 "AgsCopyAudioSignal",
80 &ags_copy_audio_signal_info,
81 0);
82
83 g_once_init_leave(&g_define_type_id__volatile, ags_type_copy_audio_signal);
84 }
85
86 return g_define_type_id__volatile;
87 }
88
89 void
ags_copy_audio_signal_class_init(AgsCopyAudioSignalClass * copy_audio_signal)90 ags_copy_audio_signal_class_init(AgsCopyAudioSignalClass *copy_audio_signal)
91 {
92 GObjectClass *gobject;
93 AgsRecallClass *recall;
94
95 ags_copy_audio_signal_parent_class = g_type_class_peek_parent(copy_audio_signal);
96
97 /* GObjectClass */
98 gobject = (GObjectClass *) copy_audio_signal;
99
100 gobject->finalize = ags_copy_audio_signal_finalize;
101
102 /* AgsRecallClass */
103 recall = (AgsRecallClass *) copy_audio_signal;
104
105 recall->run_init_pre = ags_copy_audio_signal_run_init_pre;
106 recall->run_pre = ags_copy_audio_signal_run_pre;
107 recall->run_inter = ags_copy_audio_signal_run_inter;
108 }
109
110 void
ags_copy_audio_signal_init(AgsCopyAudioSignal * copy_audio_signal)111 ags_copy_audio_signal_init(AgsCopyAudioSignal *copy_audio_signal)
112 {
113 AGS_RECALL(copy_audio_signal)->name = "ags-copy";
114 AGS_RECALL(copy_audio_signal)->version = AGS_RECALL_DEFAULT_VERSION;
115 AGS_RECALL(copy_audio_signal)->build_id = AGS_RECALL_DEFAULT_BUILD_ID;
116 AGS_RECALL(copy_audio_signal)->xml_type = "ags-copy-audio-signal";
117 AGS_RECALL(copy_audio_signal)->port = NULL;
118
119 AGS_RECALL(copy_audio_signal)->child_type = G_TYPE_NONE;
120 }
121
122 void
ags_copy_audio_signal_finalize(GObject * gobject)123 ags_copy_audio_signal_finalize(GObject *gobject)
124 {
125 /* call parent */
126 G_OBJECT_CLASS(ags_copy_audio_signal_parent_class)->finalize(gobject);
127 }
128
129 void
ags_copy_audio_signal_run_init_pre(AgsRecall * recall)130 ags_copy_audio_signal_run_init_pre(AgsRecall *recall)
131 {
132 AgsChannel *destination_channel;
133 AgsRecycling *destination_recycling;
134 AgsAudioSignal *destination;
135 AgsRecallID *parent_recall_id;
136 AgsRecallID *recall_id;
137 AgsRecyclingContext *parent_recycling_context;
138 AgsRecyclingContext *recycling_context;
139
140 AgsCopyChannelRun *copy_channel_run;
141 AgsCopyRecycling *copy_recycling;
142 AgsCopyAudioSignal *copy_audio_signal;
143
144 GObject *output_soundcard;
145
146 GList *list_start, *list;
147 GList *stream;
148
149 gdouble delay;
150 guint attack;
151 guint length;
152
153 void (*parent_class_run_init_pre)(AgsRecall *recall);
154
155 copy_audio_signal = AGS_COPY_AUDIO_SIGNAL(recall);
156
157 /* get parent class */
158 parent_class_run_init_pre = AGS_RECALL_CLASS(ags_copy_audio_signal_parent_class)->run_init_pre;
159
160 /* set flags */
161 ags_recall_unset_behaviour_flags(recall,
162 AGS_SOUND_BEHAVIOUR_PERSISTENT);
163
164 /* get some fields */
165 g_object_get(copy_audio_signal,
166 "parent", ©_recycling,
167 "output-soundcard", &output_soundcard,
168 "recall-id", &recall_id,
169 NULL);
170
171 g_object_get(copy_recycling,
172 "parent", ©_channel_run,
173 NULL);
174
175 /* channel */
176 g_object_get(copy_channel_run,
177 "destination", &destination_channel,
178 NULL);
179
180 /* recycling */
181 g_object_get(copy_recycling,
182 "destination", &destination_recycling,
183 NULL);
184
185 /* get recycling context */
186 g_object_get(recall_id,
187 "recycling-context", &recycling_context,
188 NULL);
189
190 g_object_get(recycling_context,
191 "parent", &parent_recycling_context,
192 NULL);
193
194 g_object_get(destination_channel,
195 "recall-id", &list_start,
196 NULL);
197
198 parent_recall_id = ags_recall_id_find_recycling_context(list_start,
199 parent_recycling_context);
200
201 g_list_free_full(list_start,
202 g_object_unref);
203
204 //TODO:JK: unclear
205 attack = 0;
206 delay = 0.0;
207
208 /* create new audio signal */
209 destination = ags_audio_signal_new((GObject *) output_soundcard,
210 (GObject *) destination_recycling,
211 (GObject *) parent_recall_id);
212
213 g_object_set(copy_audio_signal,
214 "destination", destination,
215 NULL);
216 ags_recycling_create_audio_signal_with_defaults(destination_recycling,
217 destination,
218 delay, attack);
219 length = 1; // (guint) (2.0 * soundcard->delay[soundcard->tic_counter]) + 1;
220 ags_audio_signal_stream_resize(destination,
221 length);
222
223 ags_connectable_connect(AGS_CONNECTABLE(destination));
224
225 destination->stream_current = destination->stream;
226
227 ags_recycling_add_audio_signal(destination_recycling,
228 destination);
229
230 #ifdef AGS_DEBUG
231 g_message("copy %x to %x", destination, parent_recall_id);
232 g_message("creating destination");
233 #endif
234
235 /* call parent */
236 parent_class_run_init_pre(recall);
237
238 /* unref */
239 g_object_unref(copy_recycling);
240
241 g_object_unref(output_soundcard);
242
243 g_object_unref(recall_id);
244
245 g_object_unref(copy_channel_run);
246
247 g_object_unref(destination_channel);
248
249 g_object_unref(destination_recycling);
250
251 g_object_unref(recycling_context);
252
253 g_object_unref(parent_recycling_context);
254 }
255
256 void
ags_copy_audio_signal_run_pre(AgsRecall * recall)257 ags_copy_audio_signal_run_pre(AgsRecall *recall)
258 {
259 AgsAudioSignal *source;
260
261 AgsCopyAudioSignal *copy_audio_signal;
262
263 void *buffer;
264
265 guint buffer_size;
266 guint format;
267
268 void (*parent_class_run_pre)(AgsRecall *recall);
269
270 copy_audio_signal = AGS_COPY_AUDIO_SIGNAL(recall);
271
272 /* get parent class */
273 parent_class_run_pre = AGS_RECALL_CLASS(ags_copy_audio_signal_parent_class)->run_pre;
274
275 /* call parent */
276 parent_class_run_pre(recall);
277
278 g_object_get(recall,
279 "source", &source,
280 NULL);
281
282 if(source->stream != NULL){
283 AgsAudioSignal *destination;
284
285 g_object_get(recall,
286 "destination", &destination,
287 NULL);
288
289 buffer = destination->stream->data;
290
291 g_object_get(destination,
292 "buffer-size", &buffer_size,
293 "format", &format,
294 NULL);
295
296 ags_audio_buffer_util_clear_buffer(buffer, 1,
297 buffer_size, ags_audio_buffer_util_format_from_soundcard(format));
298 }
299
300 g_object_unref(source);
301 }
302
303 void
ags_copy_audio_signal_run_inter(AgsRecall * recall)304 ags_copy_audio_signal_run_inter(AgsRecall *recall)
305 {
306 AgsRecycling *destination_recycling, *source_recycling;
307 AgsAudioSignal *destination, *source;
308 AgsPort *port;
309 AgsRecallID *recall_id;
310 AgsRecyclingContext *parent_recycling_context;
311 AgsRecyclingContext *recycling_context;
312
313 AgsCopyChannel *copy_channel;
314 AgsCopyChannelRun *copy_channel_run;
315 AgsCopyRecycling *copy_recycling;
316 AgsCopyAudioSignal *copy_audio_signal;
317
318 GList *note_start;
319 GList *stream_source, *stream_destination;
320
321 void *buffer_source;
322 gchar *str;
323
324 guint destination_buffer_size, source_buffer_size;
325 guint destination_samplerate, source_samplerate;
326 guint destination_format, source_format;
327 guint attack;
328 guint copy_mode;
329 gboolean is_muted;
330 gboolean resample;
331
332 GValue value = {0,};
333
334 void (*parent_class_run_inter)(AgsRecall *recall);
335
336 copy_audio_signal = AGS_COPY_AUDIO_SIGNAL(recall);
337
338 /* get parent class */
339 parent_class_run_inter = AGS_RECALL_CLASS(ags_copy_audio_signal_parent_class)->run_inter;
340
341 /* call parent */
342 parent_class_run_inter(recall);
343
344 /* get source and recall id */
345 g_object_get(recall,
346 "source", &source,
347 "recall-id", &recall_id,
348 NULL);
349
350 /* get recycling context and its parent */
351 g_object_get(recall_id,
352 "recycling-context", &recycling_context,
353 NULL);
354
355 g_object_get(recycling_context,
356 "parent", &parent_recycling_context,
357 NULL);
358
359 /* get notes */
360 g_object_get(source,
361 "note", ¬e_start,
362 NULL);
363
364 if(ags_recall_global_get_rt_safe() &&
365 parent_recycling_context != NULL &&
366 note_start == NULL){
367 g_object_unref(source);
368
369 g_object_unref(recall_id);
370
371 g_object_unref(recycling_context);
372 g_object_unref(parent_recycling_context);
373
374 return;
375 }
376
377 g_list_free_full(note_start,
378 g_object_unref);
379
380 /* get destination */
381 g_object_get(recall,
382 "destination", &destination,
383 NULL);
384
385 /* get stream */
386 stream_source = source->stream;
387
388 if(stream_source == NULL){
389 g_object_get(source,
390 "recycling", &source_recycling,
391 NULL);
392
393 if(destination != NULL){
394 g_object_get(destination,
395 "recycling", &destination_recycling,
396 NULL);
397
398 ags_recycling_remove_audio_signal(destination_recycling,
399 destination);
400
401 g_object_run_dispose((GObject *) destination);
402 g_object_unref((GObject *) destination);
403
404 g_object_unref(destination_recycling);
405 }
406
407 ags_recall_done(recall);
408
409 ags_recycling_remove_audio_signal(source_recycling,
410 source);
411 g_object_unref(source);
412
413 g_object_unref(source_recycling);
414
415 /* unref */
416 g_object_unref(source);
417
418 g_object_unref(recall_id);
419
420 g_object_unref(recycling_context);
421 g_object_unref(parent_recycling_context);
422
423 return;
424 }
425
426 if(destination == NULL){
427 g_warning("no destination");
428
429 /* unref */
430 g_object_unref(source);
431
432 g_object_unref(recall_id);
433
434 g_object_unref(recycling_context);
435 g_object_unref(parent_recycling_context);
436
437 return;
438 }
439
440 /* get related recalls */
441 g_object_get(recall,
442 "parent", ©_recycling,
443 NULL);
444
445 g_object_get(copy_recycling,
446 "parent", ©_channel_run,
447 NULL);
448
449 g_object_get(copy_channel_run,
450 "recall-channel", ©_channel,
451 NULL);
452
453 /* check muted */
454 g_object_get(copy_channel,
455 "muted", &port,
456 NULL);
457
458 g_value_init(&value, G_TYPE_FLOAT);
459 ags_port_safe_read(port,
460 &value);
461
462 is_muted = (g_value_get_float(&value) == 0.0) ? TRUE: FALSE;
463 g_value_unset(&value);
464
465 g_object_unref(port);
466
467 if(is_muted){
468 goto ags_copy_audio_signal_run_inter_END;
469 }
470
471 stream_destination = destination->stream;
472
473 if(!ags_recall_global_get_rt_safe() &&
474 stream_destination->next == NULL){
475 ags_audio_signal_add_stream(destination);
476 }
477
478
479 g_object_get(destination,
480 "buffer-size", &destination_buffer_size,
481 "samplerate", &destination_samplerate,
482 "format", &destination_format,
483 NULL);
484
485 g_object_get(source,
486 "buffer-size", &source_buffer_size,
487 "attack", &attack,
488 "samplerate", &source_samplerate,
489 "format", &source_format,
490 NULL);
491
492 copy_mode = ags_audio_buffer_util_get_copy_mode(ags_audio_buffer_util_format_from_soundcard(destination_format),
493 ags_audio_buffer_util_format_from_soundcard(source_format));
494
495 resample = FALSE;
496
497 /* check if resample */
498 buffer_source = stream_source->data;
499 attack = (destination_samplerate / source_samplerate) * attack;
500
501 if(source_samplerate != destination_samplerate){
502 void *tmp_buffer_source;
503
504 tmp_buffer_source = ags_stream_alloc(destination_buffer_size,
505 source_format);
506
507 ags_audio_buffer_util_resample_with_buffer(buffer_source, 1,
508 ags_audio_buffer_util_format_from_soundcard(source_format), source_samplerate,
509 source_buffer_size,
510 destination_samplerate,
511 destination_buffer_size,
512 tmp_buffer_source);
513
514 buffer_source = tmp_buffer_source;
515
516 resample = TRUE;
517 }
518
519 if(ags_recall_test_flags(recall, AGS_RECALL_INITIAL_RUN)){
520 ags_audio_buffer_util_copy_buffer_to_buffer(stream_destination->data, 1, attack,
521 buffer_source, 1, 0,
522 destination_buffer_size - attack, copy_mode);
523
524 ags_recall_unset_flags(recall, AGS_RECALL_INITIAL_RUN);
525 }else{
526 if(attack != 0 && stream_source->prev != NULL){
527 void *buffer_source_prev;
528
529 buffer_source_prev = stream_source->prev->data;
530
531 if(resample){
532 void *tmp_buffer_source_prev;
533
534 tmp_buffer_source_prev = ags_stream_alloc(destination_buffer_size,
535 source_format);
536
537 ags_audio_buffer_util_resample_with_buffer(buffer_source_prev, 1,
538 ags_audio_buffer_util_format_from_soundcard(source_format), source_samplerate,
539 source_buffer_size,
540 destination_samplerate,
541 destination_buffer_size,
542 tmp_buffer_source_prev);
543
544 buffer_source_prev = tmp_buffer_source_prev;
545 }
546
547 ags_audio_buffer_util_copy_buffer_to_buffer(stream_destination->data, 1, 0,
548 buffer_source_prev, 1, destination_buffer_size - attack,
549 attack, copy_mode);
550
551 if(resample){
552 free(buffer_source_prev);
553 }
554 }
555
556 ags_audio_buffer_util_copy_buffer_to_buffer(stream_destination->data, 1, source->attack,
557 buffer_source, 1, 0,
558 destination_buffer_size - attack, copy_mode);
559 }
560
561 if(resample){
562 free(buffer_source);
563 }
564
565 ags_copy_audio_signal_run_inter_END:
566 /* unref */
567 g_object_unref(source);
568
569 g_object_unref(recall_id);
570
571 g_object_unref(recycling_context);
572 g_object_unref(parent_recycling_context);
573
574 g_object_unref(destination);
575 }
576
577 /**
578 * ags_copy_audio_signal_new:
579 * @destination: the destination #AgsAudioSignal
580 * @source: the source #AgsAudioSignal
581 *
582 * Create a new instance of #AgsCopyAudioSignal
583 *
584 * Returns: the new #AgsCopyAudioSignal
585 *
586 * Since: 3.0.0
587 */
588 AgsCopyAudioSignal*
ags_copy_audio_signal_new(AgsAudioSignal * destination,AgsAudioSignal * source)589 ags_copy_audio_signal_new(AgsAudioSignal *destination,
590 AgsAudioSignal *source)
591 {
592 AgsCopyAudioSignal *copy_audio_signal;
593
594 copy_audio_signal = (AgsCopyAudioSignal *) g_object_new(AGS_TYPE_COPY_AUDIO_SIGNAL,
595 "destination", destination,
596 "source", source,
597 NULL);
598
599 return(copy_audio_signal);
600 }
601