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", &copy_recycling,
167 	       "output-soundcard", &output_soundcard,
168 	       "recall-id", &recall_id,
169 	       NULL);
170 
171   g_object_get(copy_recycling,
172 	       "parent", &copy_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", &note_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", &copy_recycling,
443 	       NULL);
444 
445   g_object_get(copy_recycling,
446 	       "parent", &copy_channel_run,
447 	       NULL);
448 
449   g_object_get(copy_channel_run,
450 	       "recall-channel", &copy_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