1 /*
2 * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero 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 * Zrythm 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 Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <stdlib.h>
21
22 #include "audio/clip.h"
23 #include "audio/encoder.h"
24 #include "audio/engine.h"
25 #include "audio/tempo_track.h"
26 #include "gui/widgets/main_window.h"
27 #include "project.h"
28 #include "utils/audio.h"
29 #include "utils/debug.h"
30 #include "utils/dsp.h"
31 #include "utils/file.h"
32 #include "utils/flags.h"
33 #include "utils/hash.h"
34 #include "utils/io.h"
35 #include "utils/math.h"
36 #include "utils/objects.h"
37 #include "utils/string.h"
38 #include "zrythm_app.h"
39
40 #include <gtk/gtk.h>
41 #include <glib/gi18n.h>
42
43 static AudioClip *
_create()44 _create ()
45 {
46 AudioClip * self = object_new (AudioClip);
47 self->schema_version =
48 AUDIO_CLIP_SCHEMA_VERSION;
49
50 return self;
51 }
52
53 /**
54 * Updates the channel caches.
55 *
56 * See @ref AudioClip.ch_frames.
57 *
58 * @param start_from Frames to start from (per
59 * channel. The previous frames will be kept.
60 */
61 void
audio_clip_update_channel_caches(AudioClip * self,size_t start_from)62 audio_clip_update_channel_caches (
63 AudioClip * self,
64 size_t start_from)
65 {
66 z_return_if_fail_cmp (
67 self->channels, >, 0);
68 z_return_if_fail_cmp (
69 self->num_frames, >, 0);
70
71 /* copy the frames to the channel caches */
72 for (unsigned int i = 0; i < self->channels; i++)
73 {
74 self->ch_frames[i] =
75 g_realloc (
76 self->ch_frames[i],
77 sizeof (float) *
78 (size_t) self->num_frames);
79 for (size_t j = start_from;
80 j < (size_t) self->num_frames; j++)
81 {
82 self->ch_frames[i][j] =
83 self->frames[j * self->channels + i];
84 }
85 }
86 }
87
88 static void
audio_clip_init_from_file(AudioClip * self,const char * full_path)89 audio_clip_init_from_file (
90 AudioClip * self,
91 const char * full_path)
92 {
93 g_return_if_fail (self);
94
95 self->samplerate =
96 (int) AUDIO_ENGINE->sample_rate;
97 g_return_if_fail (self->samplerate > 0);
98
99 AudioEncoder * enc =
100 audio_encoder_new_from_file (full_path);
101 audio_encoder_decode (
102 enc, self->samplerate, F_SHOW_PROGRESS);
103
104 size_t arr_size =
105 (size_t) enc->num_out_frames *
106 (size_t) enc->nfo.channels;
107 self->frames =
108 g_realloc (
109 self->frames, arr_size * sizeof (float));
110 self->num_frames = enc->num_out_frames;
111 dsp_copy (
112 self->frames, enc->out_frames, arr_size);
113 g_free_and_null (self->name);
114 char * basename = g_path_get_basename (full_path);
115 self->name = io_file_strip_ext (basename);
116 g_free (basename);
117 self->channels = enc->nfo.channels;
118 self->bpm =
119 tempo_track_get_current_bpm (P_TEMPO_TRACK);
120 switch (enc->nfo.bit_depth)
121 {
122 case 16:
123 self->bit_depth = BIT_DEPTH_16;
124 self->use_flac = true;
125 break;
126 case 24:
127 self->bit_depth = BIT_DEPTH_24;
128 self->use_flac = true;
129 break;
130 case 32:
131 self->bit_depth = BIT_DEPTH_32;
132 self->use_flac = false;
133 break;
134 default:
135 g_debug (
136 "unknown bit depth: %d", enc->nfo.bit_depth);
137 self->bit_depth = BIT_DEPTH_32;
138 self->use_flac = false;
139 }
140 /*g_message (*/
141 /*"\n\n num frames %ld \n\n", self->num_frames);*/
142 audio_clip_update_channel_caches (self, 0);
143
144 audio_encoder_free (enc);
145 }
146
147 /**
148 * Inits after loading a Project.
149 */
150 void
audio_clip_init_loaded(AudioClip * self)151 audio_clip_init_loaded (
152 AudioClip * self)
153 {
154 g_debug (
155 "%s: %p", __func__, self);
156
157 char * filepath =
158 audio_clip_get_path_in_pool_from_name (
159 self->name, self->use_flac, F_NOT_BACKUP);
160
161 bpm_t bpm = self->bpm;
162 audio_clip_init_from_file (self, filepath);
163 self->bpm = bpm;
164
165 g_free (filepath);
166 }
167
168 /**
169 * Creates an audio clip from a file.
170 *
171 * The name used is the basename of the file.
172 */
173 AudioClip *
audio_clip_new_from_file(const char * full_path)174 audio_clip_new_from_file (
175 const char * full_path)
176 {
177 AudioClip * self = _create ();
178
179 audio_clip_init_from_file (self, full_path);
180
181 self->pool_id = -1;
182 self->bpm =
183 tempo_track_get_current_bpm (P_TEMPO_TRACK);
184
185 return self;
186 }
187
188 /**
189 * Creates an audio clip by copying the given float
190 * array.
191 *
192 * @param name A name for this clip.
193 */
194 AudioClip *
audio_clip_new_from_float_array(const float * arr,const long nframes,const channels_t channels,BitDepth bit_depth,const char * name)195 audio_clip_new_from_float_array (
196 const float * arr,
197 const long nframes,
198 const channels_t channels,
199 BitDepth bit_depth,
200 const char * name)
201 {
202 AudioClip * self = _create ();
203
204 self->frames =
205 object_new_n (
206 (size_t) (nframes * (long) channels),
207 sample_t);
208 self->num_frames = nframes;
209 self->channels = channels;
210 self->samplerate = (int) AUDIO_ENGINE->sample_rate;
211 g_return_val_if_fail (self->samplerate > 0, NULL);
212 self->name = g_strdup (name);
213 self->bit_depth = bit_depth;
214 self->use_flac = bit_depth < BIT_DEPTH_32;
215 self->pool_id = -1;
216 dsp_copy (
217 self->frames, arr,
218 (size_t) nframes * (size_t) channels);
219 self->bpm =
220 tempo_track_get_current_bpm (P_TEMPO_TRACK);
221 audio_clip_update_channel_caches (self, 0);
222
223 return self;
224 }
225
226 /**
227 * Create an audio clip while recording.
228 *
229 * The frames will keep getting reallocated until
230 * the recording is finished.
231 *
232 * @param nframes Number of frames to allocate. This
233 * should be the current cycle's frames when
234 * called during recording.
235 */
236 AudioClip *
audio_clip_new_recording(const channels_t channels,const long nframes,const char * name)237 audio_clip_new_recording (
238 const channels_t channels,
239 const long nframes,
240 const char * name)
241 {
242 AudioClip * self = _create ();
243
244 self->channels = channels;
245 self->frames =
246 object_new_n (
247 (size_t) (nframes * (long) self->channels),
248 sample_t);
249 self->num_frames = nframes;
250 self->name = g_strdup (name);
251 self->pool_id = -1;
252 self->bpm =
253 tempo_track_get_current_bpm (P_TEMPO_TRACK);
254 self->samplerate = (int) AUDIO_ENGINE->sample_rate;
255 self->bit_depth = BIT_DEPTH_32;
256 self->use_flac = false;
257 g_return_val_if_fail (self->samplerate > 0, NULL);
258 dsp_fill (
259 self->frames, DENORMAL_PREVENTION_VAL,
260 (size_t) nframes * (size_t) channels);
261 audio_clip_update_channel_caches (self, 0);
262
263 return self;
264 }
265
266 /**
267 * Gets the path of a clip matching \ref name from
268 * the pool.
269 *
270 * @param use_flac Whether to look for a FLAC file
271 * instead of a wav file.
272 * @param is_backup Whether writing to a backup
273 * project.
274 */
275 char *
audio_clip_get_path_in_pool_from_name(const char * name,bool use_flac,bool is_backup)276 audio_clip_get_path_in_pool_from_name (
277 const char * name,
278 bool use_flac,
279 bool is_backup)
280 {
281 char * prj_pool_dir =
282 project_get_path (
283 PROJECT, PROJECT_PATH_POOL, is_backup);
284 if (!file_exists (prj_pool_dir))
285 {
286 g_critical ("%s does not exist", prj_pool_dir);
287 return NULL;
288 }
289 char * without_ext =
290 io_file_strip_ext (name);
291 char * basename =
292 g_strdup_printf (
293 "%s.%s", without_ext,
294 use_flac ? "FLAC" : "wav");
295 char * new_path =
296 g_build_filename (
297 prj_pool_dir, basename, NULL);
298 g_free (without_ext);
299 g_free (basename);
300 g_free (prj_pool_dir);
301
302 return new_path;
303 }
304
305 /**
306 * Gets the path of the given clip from the pool.
307 *
308 * @param is_backup Whether writing to a backup
309 * project.
310 */
311 char *
audio_clip_get_path_in_pool(AudioClip * self,bool is_backup)312 audio_clip_get_path_in_pool (
313 AudioClip * self,
314 bool is_backup)
315 {
316 return
317 audio_clip_get_path_in_pool_from_name (
318 self->name, self->use_flac, is_backup);
319 }
320
321 /**
322 * Writes the clip to the pool as a wav file.
323 *
324 * @param parts If true, only write new data. @see
325 * AudioClip.frames_written.
326 * @param is_backup Whether writing to a backup
327 * project.
328 */
329 void
audio_clip_write_to_pool(AudioClip * self,bool parts,bool is_backup)330 audio_clip_write_to_pool (
331 AudioClip * self,
332 bool parts,
333 bool is_backup)
334 {
335 AudioClip * pool_clip =
336 audio_pool_get_clip (
337 AUDIO_POOL, self->pool_id);
338 g_return_if_fail (pool_clip);
339 g_return_if_fail (pool_clip == self);
340
341 audio_pool_print (AUDIO_POOL);
342 g_message (
343 "attempting to write clip %s (%d) to pool...",
344 self->name, self->pool_id);
345
346 /* generate a copy of the given filename in the
347 * project dir */
348 char * path_in_main_project =
349 audio_clip_get_path_in_pool (
350 self, F_NOT_BACKUP);
351 char * new_path =
352 audio_clip_get_path_in_pool (self, is_backup);
353 g_return_if_fail (path_in_main_project);
354 g_return_if_fail (new_path);
355
356 /* whether a new write is needed */
357 bool need_new_write = true;
358
359 /* skip if file with same hash already exists */
360 if (file_exists (new_path) && !parts)
361 {
362 char * existing_file_hash =
363 hash_get_from_file (
364 new_path, HASH_ALGORITHM_XXH3_64);
365 bool same_hash =
366 self->file_hash &&
367 string_is_equal (
368 self->file_hash, existing_file_hash);
369 g_free (existing_file_hash);
370
371 if (same_hash)
372 {
373 g_debug (
374 "skipping writing to existing clip %s "
375 "in pool",
376 new_path);
377 need_new_write = false;
378 }
379 #if 0
380 else
381 {
382 g_critical (
383 "attempted to overwrite %s with a "
384 "different clip",
385 new_path);
386 return;
387 }
388 #endif
389 }
390
391 /* if writing to backup and same file exists in
392 * main project dir, copy (first try reflink) */
393 if (need_new_write && self->file_hash && is_backup)
394 {
395 bool exists_in_main_project = false;
396 if (file_exists (path_in_main_project))
397 {
398 char * existing_file_hash =
399 hash_get_from_file (
400 path_in_main_project,
401 HASH_ALGORITHM_XXH3_64);
402 exists_in_main_project =
403 string_is_equal (
404 self->file_hash, existing_file_hash);
405 g_free (existing_file_hash);
406 }
407
408 if (exists_in_main_project)
409 {
410 /* try reflink */
411 g_debug (
412 "reflinking clip from main project "
413 "('%s' to '%s')",
414 path_in_main_project, new_path);
415
416 if (file_reflink (
417 path_in_main_project, new_path) != 0)
418 {
419 g_message (
420 "failed to reflink, copying "
421 "instead");
422
423 /* copy */
424 GFile * src_file =
425 g_file_new_for_path (
426 path_in_main_project);
427 GFile * dest_file =
428 g_file_new_for_path (new_path);
429 GError * err = NULL;
430 g_debug (
431 "copying clip from main project "
432 "('%s' to '%s')",
433 path_in_main_project, new_path);
434 if (g_file_copy (
435 src_file, dest_file, 0, NULL, NULL,
436 NULL, &err))
437 {
438 need_new_write = false;
439 }
440 else /* else if failed */
441 {
442 g_warning (
443 "Failed to copy '%s' to '%s': %s",
444 path_in_main_project, new_path,
445 err->message);
446 }
447 } /* endif reflink fail */
448 }
449 }
450
451 if (need_new_write)
452 {
453 g_debug (
454 "writing clip %s to pool "
455 "(parts %d, is backup %d): '%s'",
456 self->name, parts, is_backup, new_path);
457 audio_clip_write_to_file (
458 self, new_path, parts);
459
460 if (!parts)
461 {
462 /* store file hash */
463 g_free_and_null (self->file_hash);
464 self->file_hash =
465 hash_get_from_file (
466 new_path, HASH_ALGORITHM_XXH3_64);
467 }
468 }
469
470 g_free (path_in_main_project);
471 g_free (new_path);
472
473 audio_pool_print (AUDIO_POOL);
474 }
475
476 /**
477 * Writes the given audio clip data to a file.
478 *
479 * @param parts If true, only write new data. @see
480 * AudioClip.frames_written.
481 *
482 * @return Non-zero if fail.
483 */
484 int
audio_clip_write_to_file(AudioClip * self,const char * filepath,bool parts)485 audio_clip_write_to_file (
486 AudioClip * self,
487 const char * filepath,
488 bool parts)
489 {
490 g_return_val_if_fail (self->samplerate > 0, -1);
491 size_t before_frames =
492 (size_t) self->frames_written;
493 long ch_offset =
494 parts ? self->frames_written : 0;
495 long offset = ch_offset * self->channels;
496 int ret =
497 audio_write_raw_file (
498 &self->frames[offset], ch_offset,
499 parts ?
500 (self->num_frames - self->frames_written) :
501 self->num_frames,
502 (uint32_t) self->samplerate,
503 self->use_flac, self->bit_depth,
504 self->channels, filepath);
505 audio_clip_update_channel_caches (
506 self, before_frames);
507
508 if (parts && ret == 0)
509 {
510 self->frames_written = self->num_frames;
511 self->last_write = g_get_monotonic_time ();
512 }
513
514 /* TODO move this to a unit test for this
515 * function */
516 if (ZRYTHM_TESTING)
517 {
518 AudioClip * new_clip =
519 audio_clip_new_from_file (filepath);
520 if (self->num_frames != new_clip->num_frames)
521 {
522 g_warning (
523 "%zu != %zu",
524 self->num_frames, new_clip->num_frames);
525 }
526 float epsilon = 0.0001f;
527 g_warn_if_fail (
528 audio_frames_equal (
529 self->ch_frames[0], new_clip->ch_frames[0],
530 (size_t) new_clip->num_frames,
531 epsilon));
532 g_warn_if_fail (
533 audio_frames_equal (
534 self->frames, new_clip->frames,
535 (size_t)
536 new_clip->num_frames * new_clip->channels,
537 epsilon));
538 audio_clip_free (new_clip);
539 }
540
541 return ret;
542 }
543
544 /**
545 * Returns whether the clip is used inside the
546 * project.
547 *
548 * @param check_undo_stack If true, this checks both
549 * project regions and the undo stack. If false,
550 * this only checks actual project regions only.
551 */
552 bool
audio_clip_is_in_use(AudioClip * self,bool check_undo_stack)553 audio_clip_is_in_use (
554 AudioClip * self,
555 bool check_undo_stack)
556 {
557 for (int i = 0; i < TRACKLIST->num_tracks; i++)
558 {
559 Track * track = TRACKLIST->tracks[i];
560 if (track->type != TRACK_TYPE_AUDIO)
561 continue;
562
563 for (int j = 0; j < track->num_lanes; j++)
564 {
565 TrackLane * lane = track->lanes[j];
566
567 for (int k = 0; k < lane->num_regions; k++)
568 {
569 ZRegion * r = lane->regions[k];
570 if (r->id.type != REGION_TYPE_AUDIO)
571 continue;
572
573 if (r->pool_id == self->pool_id)
574 return true;
575 }
576 }
577 }
578
579 if (check_undo_stack)
580 {
581 if (undo_manager_contains_clip (
582 UNDO_MANAGER, self))
583 {
584 return true;
585 }
586 }
587
588 return false;
589 }
590
591 typedef struct AppLaunchData
592 {
593 GFile * file;
594 GtkAppChooser * app_chooser;
595 } AppLaunchData;
596
597 static void
app_launch_data_free(AppLaunchData * data,GClosure * closure)598 app_launch_data_free (
599 AppLaunchData * data,
600 GClosure * closure)
601 {
602 free (data);
603 }
604
605 static void
on_launch_clicked(GtkButton * btn,AppLaunchData * data)606 on_launch_clicked (
607 GtkButton * btn,
608 AppLaunchData * data)
609 {
610 GError * err = NULL;
611 GList * file_list = NULL;
612 file_list =
613 g_list_append (file_list, data->file);
614 GAppInfo * app_nfo =
615 gtk_app_chooser_get_app_info (data->app_chooser);
616 bool success =
617 g_app_info_launch (
618 app_nfo, file_list, NULL, &err);
619 g_list_free (file_list);
620 if (!success)
621 {
622 g_message ("app launch unsuccessful");
623 }
624 }
625
626 /**
627 * Shows a dialog with info on how to edit a file,
628 * with an option to open an app launcher.
629 *
630 * When the user closes the dialog, the clip is
631 * assumed to have been edited.
632 *
633 * The given audio clip will be free'd.
634 *
635 * @note This must not be used on pool clips.
636 *
637 * @return A new instance of AudioClip if successful,
638 * NULL, if not.
639 */
640 AudioClip *
audio_clip_edit_in_ext_program(AudioClip * self)641 audio_clip_edit_in_ext_program (
642 AudioClip * self)
643 {
644 GError * err = NULL;
645 char * tmp_dir =
646 g_dir_make_tmp (
647 "zrythm-audio-clip-tmp-XXXXXX", &err);
648 g_return_val_if_fail (tmp_dir, NULL);
649 char * abs_path =
650 g_build_filename (
651 tmp_dir, "tmp.wav", NULL);
652 audio_clip_write_to_file (
653 self, abs_path, false);
654 audio_clip_free (self);
655
656 GFile * file =
657 g_file_new_for_path (abs_path);
658 err = NULL;
659 GFileInfo * file_info =
660 g_file_query_info (
661 file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
662 G_FILE_QUERY_INFO_NONE, NULL, &err);
663 g_return_val_if_fail (file_info, false);
664 const char * content_type =
665 g_file_info_get_content_type (file_info);
666
667 GtkWidget * dialog =
668 gtk_dialog_new_with_buttons (
669 _("Edit in external app"),
670 GTK_WINDOW (MAIN_WINDOW),
671 GTK_DIALOG_MODAL
672 | GTK_DIALOG_DESTROY_WITH_PARENT,
673 _("_OK"), GTK_RESPONSE_ACCEPT,
674 _("_Cancel"), GTK_RESPONSE_REJECT,
675 NULL);
676
677 /* populate content area */
678 GtkWidget * content_area =
679 gtk_dialog_get_content_area (
680 GTK_DIALOG (dialog));
681 GtkWidget * main_box =
682 gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
683 gtk_widget_set_margin_start (main_box, 4);
684 gtk_widget_set_margin_end (main_box, 4);
685 gtk_widget_set_margin_top (main_box, 4);
686 gtk_widget_set_margin_bottom (main_box, 4);
687 gtk_container_add (
688 GTK_CONTAINER (content_area), main_box);
689
690 GtkWidget * lbl = gtk_label_new ("");
691 gtk_label_set_selectable (GTK_LABEL (lbl), true);
692 char * markup =
693 g_markup_printf_escaped (
694 _("Edit the file at <u>%s</u>, then "
695 "press OK"),
696 abs_path);
697 gtk_label_set_markup (
698 GTK_LABEL (lbl), markup);
699 gtk_container_add (GTK_CONTAINER (main_box), lbl);
700
701 GtkWidget * launch_box =
702 gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
703 gtk_widget_set_halign (
704 launch_box, GTK_ALIGN_CENTER);
705 GtkWidget * app_chooser_button =
706 gtk_app_chooser_button_new (content_type);
707 gtk_container_add (
708 GTK_CONTAINER (launch_box), app_chooser_button);
709 GtkWidget * btn =
710 gtk_button_new_with_label (_("Launch"));
711 AppLaunchData * data = object_new (AppLaunchData);
712 data->file = file;
713 data->app_chooser =
714 GTK_APP_CHOOSER (app_chooser_button);
715 g_signal_connect_data (
716 G_OBJECT (btn), "clicked",
717 G_CALLBACK (on_launch_clicked), data,
718 (GClosureNotify) app_launch_data_free, 0);
719 gtk_container_add (
720 GTK_CONTAINER (launch_box), btn);
721 gtk_container_add (
722 GTK_CONTAINER (main_box), launch_box);
723
724 gtk_widget_show_all (content_area);
725 int ret =
726 gtk_dialog_run (GTK_DIALOG (dialog));
727 gtk_widget_destroy (GTK_WIDGET (dialog));
728 if (ret != GTK_RESPONSE_ACCEPT)
729 {
730 g_debug ("cancelled");
731 return NULL;
732 }
733
734 /* ok - reload from file */
735 self = audio_clip_new_from_file (abs_path);
736 g_return_val_if_fail (self, false);
737
738 return self;
739 }
740
741 /**
742 * To be called by audio_pool_remove_clip().
743 *
744 * Removes the file associated with the clip and
745 * frees the instance.
746 *
747 * @param backup Whether to remove from backup
748 * directory.
749 */
750 void
audio_clip_remove_and_free(AudioClip * self,bool backup)751 audio_clip_remove_and_free (
752 AudioClip * self,
753 bool backup)
754 {
755 char * path =
756 audio_clip_get_path_in_pool (
757 self, F_NOT_BACKUP);
758 g_message ("removing clip at %s", path);
759 g_return_if_fail (path);
760 io_remove (path);
761
762 audio_clip_free (self);
763 }
764
765 AudioClip *
audio_clip_clone(AudioClip * src)766 audio_clip_clone (
767 AudioClip * src)
768 {
769 AudioClip * self = object_new (AudioClip);
770 self->schema_version = AUDIO_CLIP_SCHEMA_VERSION;
771
772 self->name = g_strdup (src->name);
773 self->file_hash = g_strdup (src->file_hash);
774 self->bpm = src->bpm;
775 self->bit_depth = src->bit_depth;
776 self->use_flac = src->use_flac;
777 self->samplerate = src->samplerate;
778 self->pool_id = src->pool_id;
779
780 return self;
781 }
782
783 /**
784 * Frees the audio clip.
785 */
786 void
audio_clip_free(AudioClip * self)787 audio_clip_free (
788 AudioClip * self)
789 {
790 object_zero_and_free (self->frames);
791 for (unsigned int i = 0; i < self->channels; i++)
792 {
793 object_zero_and_free_if_nonnull (
794 self->ch_frames[i]);
795 }
796 g_free_and_null (self->name);
797 g_free_and_null (self->file_hash);
798
799 object_zero_and_free (self);
800 }
801